函数和闭包之偏应用函数

偏应用函数(Partial Applied Function)的定义:偏应用函数就是缺少部分或全部参数的函数
尽管前面的例子里下划线(_)替代的只是单个参数,但你还可以使用单个下划线替换整个参数列表。再来看看以前的例子(注:使用单个下划线替换整个参数列表的情况,一般是函数配合下划线一起使用的。下划线替换了它前面这个函数的整个参数列表):

093932_PQE1_168814.jpg

红框中使用的下划线其实代表println的整个参数列表。我们再来看看另一种写法的:

095420_vdRa_168814.jpg

上例写成:println(_),或者写成:println _,都代表用下划线 _ 替换println方法的整个参数列表

Scala把这种短格式直接看作是你输入了下列代码:

100012_YPsQ_168814.jpg

因此以这两种方式使用下划线时,你就是正在写一个偏应用函数。


Scala里,当你调用函数,传入任何需要的参数,实际是在把函数应用到参数上。如:

101431_R3Ts_168814.jpg

你就可以把函数sum应用到参数1、2和3上,如下:

101542_GQM8_168814.jpg

偏应用函数是一种表达式,你不需要提供函数需要的所有参数。代之以仅提供部分不提供所需参数。比如,要创建调用sum的偏应用函数表达式,而不提供任何3个所需参数,只要在“sum”之后放一个下划线(_)即可。然后可以把得到的函数存入变量。如下:

101748_N2vh_168814.jpg

有了这个代码,scala编译器以偏应用函数表达式sum _实例化一个带缺失3个整数参数的函数值,并把这个新的函数值的索引赋值给变量a。当你把这个新函数值应用于3个参数之上时,它就转而调用sum,并传入这3个参数:

102619_EByu_168814.jpg

实际发生的事情是这样的:名为a的变量指向一个函数值对象。这个函数值是由scala编译器依照偏应用函数表达式sum _,自动产生的类的一个实例。编译器产生的类有一个apply方法带3个参数这就是为什么可以直接用a带括号与参数像函数一样调用的原因)。之所以带3个参数是因为sum _表达式缺少3参数。Scala编译器把表达式a(3,4,5)翻译成对函数值的apply方法的调用,传入3个参数3,4,5。因此a(3,4,5)是下列代码的短格式:

102909_fdxJ_168814.jpg

Scala编译器根据表达式sum _ 自动产生的类里的apply方法,简单地把这3个缺失的参数转到sum函数,并返回结果。本例中,apply调用了sum(3,4,5),并返回结果12。


这种一个下划线代表全部参数列表的表达式另一种用途就是把它当作转换def为函数值的方式。如果你有一个函数,如:sum(a:Int,b:Int,c:Int):Int,你可以把它“包装”成与apply方法具有同样的参数列表和结果类型的函数值。当你把这个函数值应用到某些参数上时,它依次把sum应用到同样的参数,并返回结果。尽管不能方法嵌套函数赋值给变量当作参数传递给其它方法,但是如果你通过在函数名称后面加下划线(_)的方式把方法或嵌套函数包装在函数值中,就可以做到了。下面来看几个例子。

例子1:

103744_35Ta_168814.jpg

例子2:

103909_QjZ6_168814.jpg

其实例子1与例子2是一样的。只不过,例子1把函数值printNum _赋值给了变量c,再把变量c传参给fun函数的,比例子2多了一个过程。注意fun(k:(Int) => String,n:Int)这个函数的定义,它的第一个参数是一个函数:k是函数名,(Int)是参数,这里只有一个整数类型参数。=>之后的String是该函数的返回类型。
其实,例子2还可以进一步简化 ,如例3:

104245_L77T_168814.jpg

看看上面,下划线也省略了,为什么?我们稍后会讲到。
再看一个嵌套函数被“包装”成函数值的例子,如下:

104440_OQZT_168814.jpg


现在我们接着先前sum _那个例子。在sum _的例子里它没有应用于任何参数。不过还可以通过提供某些但不是全部需要的参数表达一个偏应用函数。如下:

104806_Qzwy_168814.jpg

这个例子里,你提供了第一个和最后一个参数给sum,但中间参数缺失。因为仅有一个参数缺失,scala编译器会另产生一个新的函数类,其apply方法带一个参数。在使用一个参数调用的时候,这个产生的函数类的apply方法调用sum,传入1、传递给函数的参数和3。如下:

105000_bicA_168814.jpg

这个例子里,b.apply调用了sum(1,2,3)。

如果你正在写一个省略所有参数的偏应用函数表达式,如:println _或 sum _,而且在代码的那个地方正需要一个函数,你可以省略下划线从而表达得更简明。如例:

105351_0Dew_168814.jpg

这种格式仅在需要写函数的地方,如例子中的fun调用。编译器知道这种情况需要一个函数,因为fun需要一个函数作为参数传入。在不需要函数的情况下,尝试使用这种格式将引发一个编译错误,如例:

105902_CjMb_168814.jpg


为什么要使用尾下划线?
Scala的偏应用函数语法凸显了scala与经典函数式语言之间(如:haskell)的设计折中的差异。在经典函数式语言中,偏应用函数是很常规的语法应用。更进一步说,这些语言拥有非常严格的静态类型系统,能够暴露出你在使用偏应用函数中可能犯的所有错误。Scala与指令式语言(如:java)关系近得多,在这些语言中没有应用所有参数的方法会被认为是错误的。进一步说,子类型推断的面向对象的传统和全局的根类型能接受一些被经典函数式语言认为是错误的程序。
举例来说,如果你误以为List的drop(n:Int)方法是drop(),那么你会忘记需要传递给drop方法一个数字。你或许会写:print(drop)。如果scala采用偏应用函数在哪儿都OK的经典函数式传统,这个代码就将通过 类型检查。然而,你会惊奇地发现这个print语言打印的输出将总是<function>!可能发生的事情是表达式drop将被看作是函数对象。因为print可以带任何类型对象,这个代码可以编译通过,但会产生出乎意料的结果
为了避免这样的情况,scala需要你指定显示省略的函数参数,尽管标志简单到仅用一个“_”。也只有在需要函数类型的地方,scala才允许你省略这个仅用的 _
根据上面的举例,我测试了一下,如果忘记传递参数,也不加下划线,scala并不认识,是报错的(新版本中,已经改了吗??我看的是scala编程第一版的书!!!)。如例:

110942_vXBb_168814.jpg

再看下面这个修改:

111020_J55Q_168814.jpg

结果:

111107_0VWD_168814.jpg


转载于:https://my.oschina.net/fhd/blog/276724

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值