码迷,mamicode.com
首页 > 其他好文 > 详细

Scala函数特性

时间:2016-04-01 18:23:04      阅读:254      评论:0      收藏:0      [点我收藏+]

标签:

通常情况下,函数的参数是传值参数;即参数的值在它被传递给函数之前被确定。但是,如果我们需要编写一个接收参数不希望马上计算,直到调用函数内的表达式才进行真正的计算的函数。对于这种情况,Scala提供按名称参数调用函数。

示例代码如下:

技术分享

 

结果:

技术分享

 

在代码中,如果定义函数的时候,传入参数不是传入的值,而是传入的参数名称(如代码中使用t: => Long而不是t: Long),在调用该函数时,不会立即执行和参数有关的计算,而是到参数真正使用到的时候才进行计算。

 

结果说明:主函数调用delayed函数后,并不立即执行参数(time()函数的结果),而是跳过,直接执行delayed函数的第一行,到第二行真正使用到t时,才执行time()函数获取t的值,故有上述结果。

 

如果将t: => Long改成t: Long,则按照值传递进行计算,结果如下:

技术分享

此时主函数调用了delayed函数后就直接先执行time()函数获取t的值

 

另一个示例代码如下:

技术分享

这里调用了一个log函数,传入的参数中有1/0,按理说应该会报异常,但实际结果却是代码顺利的执行完成。为什么呢?这是因为我们在定义log函数的时候使用了“按名称传递参数”,只有到实际执行参数相关的计算时才会检查是否有异常。而代码中if(logEnable) println(msg)这一行代码实际根本就不会执行,自然也就不会存在检查异常的问题了

 

如果将msg: =>String改为msg: String,则执行代码就会报错

技术分享

因为在执行到log(MSG +1 / 0)这一句时就直接计算了1/0,自然就会报错了

 

使用“按名称传递参数”方式的优点是:1.减少不必要的计算; 2.减少异常

在正常的函数调用中,调用参数在调用函数中是按其定义时的参数顺序进行一一匹配。如果需要按不同的顺序传递参数,就要使用到scala的一种函数特性——命名参数。

命名参数使用方法很简单,即在调用函数时,指定参数名并进行赋值。示例代码如下:

技术分享

 

结果:

技术分享

 

从代码和结果中就可以看出使用命名参数的优点:在需要时可以任意的指定函数中某个参数的值,而不必将此参数之前的参数都赋值一遍。


scalajava一样,在定义函数的时候支持接收可变长参数列表,即最后一个参数的可以被重复。示例代码如下:

技术分享

 

结果:

技术分享

在此代码中我们定义函数printInfo接收变长参数列表,其最后一个参数names可以根据实际情况进行传参(这里我们传了3个实参)。注意,函数可变参数只能是该函数的最后一个参数(否则不能识别参数长度,这个应该很好理解)。

printInfo函数被声明的参数类型names: String*实际是数组[字符串]

scala中,函数是“头等公民”,几乎所有的操作都是以函数形式进行。同样的,能够在变量中存放函数(听上去很神奇吧)。示例如下:

技术分享

本段代码将ceil函数赋值给fun变量,ceil后面的_表名这是一个函数,而不是碰巧忘记给它传参。

 

scala中,不需要为每个函数命名,这种没有命名的函数叫做匿名函数。如何进行匿名函数的定义呢?示例如下:

(x:Double) => 3 * x

这就是一个匿名函数。就好像在scala中能够把函数赋值给变量一样,我们可以把匿名函数赋值给变量:

valtriple = (x: Double) => 3 * x

这样的方式跟使用def定义函数一样:

deftriple(x: Double) = 3 * x

但是好处就是能够不给函数命名,就能直接将它传递给另一个函数。这种方法在使用mapfilter等函数时非常常用:

技术分享


由于scala有可以将函数作为参数传递给函数的特性,故从中引出了一种简单的封装模式——借贷模式。代码示例如下:

技术分享

在此代码中,我们将BufferedWriter进行了封装,只要调用autoWrite函数,传入适当的参数,就可以完成字符串写入文件,而且不需要关闭BufferedWriter。使用借贷模式是对系统资源操作的封装,为了防止资源不被安全释放,其次用户不用考虑资源来自何处,如何归还等问题,只需要使用就行了。


带函数参数的函数由于是一个接受函数参数的函数,故被称为高阶函数,像之前讲到的map()函数就是高阶函数。如下例所示:

技术分享

上述代码中,apply函数接受一个函数f作为参数,接受一个Int类型的参数,进行f(v)运算,在下面又给出了f具体的定义(layout函数)。

 

同样的,高阶函数也可以产出另一个函数(即返回结果为一个函数,而不是某个值或对象),如下例所示:

技术分享

这里函数rectangle的输出是一个计算矩形周长的函数,矩形长已固定。

 

 

在高阶函数中,经常将只需要执行一次的函数定义为匿名函数作为参数传递给高阶函数,就好像map()filter()等高阶函数中经常能看到使用了匿名函数作为参数。匿名函数在这里有一个特性能够帮助我们写出更容易阅读的函数——参数推断

正常情况下,我们使用匿名函数的方式如下:

技术分享

即在map函数中定义匿名函数(a: Double) => a * 3,但是由于map函数知道你传入的是一个类型为(Double)=> Double类型的函数,故可以简化为下面的代码:

技术分享

并且如果匿名函数只有一个参数,则可以省略(),继续简化:

技术分享

在此基础上,如果参数在=>右边只出现了一次,则可以用_替换它:

技术分享


柯里化是指将原来接收两个参数的函数变成接收一个参数的函数的过程,新的函数返回一个以原有第二个参数作为参数的函数。是不是有种被绕晕了的感觉,先别急,先看一个示例:

技术分享

 

结果:

技术分享

 

这里可以看出,柯里化函数与多个参数的函数具有相同的功能,这中间有一个“应用部分函数”,或者叫“偏应用函数”,这个函数multipleOf4表示固定了两个参数中的一个,部分提供了函数mul所需要的参数,而不是全部提供。柯里化函数在理解上比较偏向于这样的逻辑。

那么,为什么要使用柯里化呢?首先,柯里化可以让我们构造出更像原生语句提供的功能的代码(就像我们在上面说的那样);第二点也是更重要的一点,就是函数柯里化后,参数相对独立了,这样就可以对函数的某个参数单独提供更多的类型推断信息。如下例所示:

技术分享

这里的corresponds函数就是柯里化函数,其定义如下:

技术分享

使用柯里化后,该函数柯里化后单独对第二个参数进行了更详细的功能定义,丰富了函数的功能



Scala函数特性

标签:

原文地址:http://blog.csdn.net/jianghuxiaojin/article/details/51036322

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!