Scala函数式编程原理 第二课 编程的本质(Elements of programing)

        从这周开始,我将开始学习Scala编程,我们将由浅入深的学习这门新的语言和函数式编程范式。这堂课的大部分内容对你来讲将会非常的简单易懂,因为这都是你熟悉的东西。但是,也有一些东西是对以后学习打下根基的东西,特别像是我们称之为“代替模型”的求值前的模型等知识,将会对以后的课程非常的重要,希望大家多多注意。好的,让我们开始吧:

        每一门非凡的计算机语言都有以下几点:

       1、表达最简单数据单元的原始表达式

       2、提供各种结合表达式的方式

       3、提供抽象这些表达式的途径,可以让这些表达式抽象成名字然后被调用等等。

       运用函数式编程有点像用计算器,一种很好的学习方式就是用语言本身提供的交互式的shell,你可以利用它书写一些表达式,它会返回给你所写表达式的值,Scala提供的是叫REPL的小工具。(Read-Eval-Print-Loop)想要启动Scala的REPL工具只需在命令行下敲出“scala”就可以:

     

         然后,你就可以写Scala了。如果出现以上的东西,表明你的Scala安装正确了,但是在我们的课程中,我们不会讲Scala基础安装的东西,这不属于这堂课的范畴,我们只会 讲SBT(Scala 包管理工具,类似于Java中的Maven)等东西的安装。在SBT中,你照样可以得到类似于这个REPL交互命令行,但是你输入的东西将是“sbt console”而不是“Scala”。

    在Scala的REPL下,你可以输入“34+65”;或者定义一个函数,都会返回相应的值:

  

    

 好的,下面让我们来一起看到了数据的求值,数据的求值遵循的是代数的基本法则,对于一个非原始表达式的求值遵循以下原则:

  1、用最左边的操作符,

  2、对操作数求值(从左往右)

  3、把操作符运用于操作数

  那么,对于一个name的求值是怎样的呢?对于一个name的求值,就是对于定义这个name右边的内容的求值。 求值过程一旦遇到返回值,则求值过程就会终止。在此时刻,返回的值就是一个数字(如上图)。之后,我们还会考虑其他类型的返回值。

  下面就是一个数学表达式的求值:

   (2*PI)*radius

  首先我们会求出PI的值

     (2*3.14159)*radius

   然后我们会求出括号中的值

    6.28318*radius

   然后求出radius的值

   6.28318*10

   最后得到结果,并返回结果

  62.8318

  定义表达式(definitions )还可以包含参数,我们可以利用“def square“ 语句来定义一个square函数,然后传一个double类型的参数x,等号右边的x都是参数传来的x,于是咱们可以算得square(2)等于2乘以2等于4,以此类推,得到square(5+4)等于81.0,square(square(4))等于256.0。我们还可以定义square的和,如下所示。

我们可以发现,在最后一行,函数的参数后面跟了一个数值类型,这个数值类型在参数的冒号之后,在Scala中,函数的参数符合这种规定。你也可以给一个此函数的返回值,在Scala中是写在参数的后面,如下所示:

def pow(x:Double, y: Int): Double = ...

Scala的原始数据类型都来自于Java,但是是大写的:

Int                  32位整型数字

Double          64位浮点型数字

Boolean        布尔型的值 (true或者是false)

 

那么一个函数程序是如何求值的呢?

程序的参数化的函数的求值和操作数的求值方式类似:

1、从左到右依次求出所有函数参数的值

2、把函数程序由函数右边的内容所代替,函数的结果都是等号右边得到的结果

3、与此同时,把形参全部换成真实的参数

下面,我们通过一个例子来看一下:

sumOfSquares(3, 2+2)    

==>    sumOfSquare(3, 4)

==>    square(3) + square(4)

==>    3 * 3 + square(4)   

==>     9  +   square(4)    

==>     9 +  16                  

==>     25

这种格式的表达式求值被称为"代替模型"或者“替换模型”。

这个想法成为一种模型的原因在于:表达式可以等价于一个值。(所有表达式都是值)

这可以被所有表达式所使用,只要他们没有副作用。什么是副作用?一个副作用肯定调用了是表达式,例如:c是一个可变变量,“c++”的意思就是每次运行“c++”c就会加一。“C++”这个表达式就是有副作用的,因为没用一次,就会产生新的值,一个表达式无论什么时候调用,都返回同一个值,这样的表达式就可以被称之为没有副作用。

这种“代替模型”在lamda表达式中得到了很好的实现,拉姆达表达式是函数式编程打下了根基。

总结:一旦我们有了“代替模型”,另外一个问题就出现了,是否每一个表达式都能转化成单独的一个值(在有限的很少的步骤之内)?

事实上,答案是:不行。下面有一个反例:

有一个简单的函数名字叫loop,返回值是Int型,指向的却是loop

def loop:Int = loop

这会出现什么情况呢?根据我们的取值流程,我们必须求出函数的值:把函数右边的求值过程代替左边的函数名。但是,右边的步骤却又是loop。

loop ===> loop

所以,我们把表达式没有简化到一个值,而是又转到了自己身上,并且这种转化将会一直进行下去。。。

loop ===> ...

另外一种方式去更形象化的理解这种循环步骤,从自身名字开始相当于画了一个圆圈,然后又从头开始画圈。。。

 

解释器会把函数的参数在重新运行函数程序的时候减少成为一个值,这不是唯一的解决方案。换句话说,我们可以提供一个不减少参数的方式,例如:

sumOfSquares(3, 2+2)

==>   square(3) +  square(2 + 2)

==>   3*3   +   square(2+2)

==>   9   +  square(2+2)

==>   9   +  (2+2) * (2+2)

==>   9   +   4 * 4

==>  25

现在你发现了两种方式去对相同的表达式求值,第一种方式叫做 all by value, 第二种方式叫做 call by name。

两种方式都会被简化成了一个相同的数值,只要满足以下条件:

1、被简化的表达式包含纯函数,

2、两种求值方式都会终止而不是无线循环。

call by value 有一个特点就是会一次性的求出所有参数的数值。

call by name 有一个特点是如果说函数的参数没有被用到,它就不会求出它的值,参数只有在被用到的时候,才会去求值。

下面有一个问题:有如下函数:

def test (x: Int, y: Int) = x * x

对于一下表达式的使用,指出"call by name"和"call by value"哪种求值方式更快?(或者说有更少的求值步骤?)

test(2,3)

test(3+4, 8)

test(7, 2*4)

test(3+4, 2*4)

让我们来一起看看这个问题,让我们从第一个开始:

test(2,3)

call by value:==> 2*2 ==> 4

call by name:==> 2*2  ==>4

很显然,这种方式call by name和call by value得到的步骤都是以上的方式,所以两种求值方式所用的步骤是一样的。

来看第二种:

test(3+4, 8)

call by value: ==> test(7, 8)      ==> 7*7        ==>49

call by name: ==>(3+4) * (3+4)==> 7*(3+4)  ==>7*7 ==> 49

很显然,call by value方式取胜

来看第三种:

test(7, 2*4)

call by value : ==> test(7,8) ==> 7*7  ==>49

call by name: ==>  7*7        ==>49

call by name取胜。我们来看最后一个:

test(3+4, 2*4)

call by value:==> test(7, 2*4)   ==>  test(7,8) ==> 7*7 ==>49

call by name:==>(3+4) * (3+4) ==>  7*(3+4)   ==>7*7 ==> 49

又是一样的

 

 

 

 

      

视频地址:https://www.coursera.org/learn/progfun1/lecture/vzbJj/lecture-1-2-elements-of-programming

 

更多精彩内容,请关注我的微信公众账号 互联网技术窝

      

 

 

    

      

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值