Scala函数的柯里化

头条号:浩渺烟波
关注可了解更多大数据、Android技术,黑科技,以及理财,问题或建议,请头条号留言;


在函数式编程中,函数是一等公民。 函数可以作为参数传入其他函数,函数的返回值也可以是函数,函数里面也可以嵌套函数。这些高阶函数在scala中被称为函数值。 闭包是函数值的特殊形式,因为他会绑定到另外一个作用域上线文定义的变量上。

Scala的匿名函数:

匿名函数的语法很简单: 就是箭头左边是参数列表,右边是函数体。
比如:

<code>valinc=(x:Int)=>x+1
scala>inc(7)
res10:Int=8
/<code>

也可以不设置参数列表:

<code>scala>valout=()=>print(10)
scala>out()
10
/<code>


Scala函数的柯里化

懵逼没


Scala函数的柯里化:

函数的柯里化是指将原来是两个参数的函数变为一个参数的过程,但是这个一个参数的函数返回的是以第二参数为参数的匿名函数:
比如定义一个函数:

<code>defadd(x:Int,y:Int)=x+y
/<code>

那么把这个add函数变一下:

<code>defadd(x:Int)(y:Int)=x+y
/<code>

上面add的函数变化的过程就是函数的柯里化,调用过程如下:

<code>scala>add(10)(1)
res18:Int=11
/<code>

add(10)(1)实际上是依次调用两个普通的函数(非柯里化的函数)

这个柯里化的函数最先应该是演变成这么如下的方法:

<code>defadd(x:Int)=(y:Int)=>x+y
/<code>

所以实际上调用第一次参数返回的应该是一个匿名函数,一个以传递Int类型的匿名函数,接着才是函数的真正运算

<code>scala>varreuslt=add(10)_
reuslt:Int=>Int=$$Lambda$1065/164865953@1c411474

scala>reuslt(1)
res2:Int=11
/<code>
<code>scala>add(10)(1)
res18:Int=11
/<code>

两个参数的函数可以拆分,同理三个参数的函数同样也可以柯里化:

<code>scala>defadd(x:Int)(y:Int)(z:Int)=x+y+z
add:(x:Int)(y:Int)(z:Int)Int

scala>add(10)(10)(10)
res19:Int=30
/<code>


Scala函数的柯里化


简单看一个柯里化函数函数,集合中foldLeft()

<code>override/*TraversableLike*/
deffoldLeft[B](z:B)(op:(B,A)=>B):B=
foldl(0,length,z,op)
/<code>

这个函数在集合中很有用,简单分析下B表示泛型,后面传递一个B类型的参数Z,柯里化之后后面是一个匿名函数,传递两个参数,返回一个B类型的参数,在看一下他的调用:

<code>@tailrec
privatedeffoldl[B](start:Int,end:Int,z:B,op:(B,A)=>B):B=
if(start==end)z
elsefoldl(start+1,end,op(z,this(start)),op)
/<code>

用到了递归,如果开始位置和结束位置是相等,返回z递归到了终点。否则的话继续递归,start每次加1,然后调用op这个匿名函数,this[start]取出集合中的数据进行函数操作,操作的结果就是下一次调用的参数Z,所以 我们可以利用这个folLeft求集合的最大值呀,最小值呀,求和啊 什么的挺方便“:

<code>valarray=Array(1,2,3,4,5)
valresult=array.foldLeft(0){(sum,element)=>sum+element}
println(s"foldLeft=${result}")
/<code>

还有一个foldRight,跟这个类似,一个是从开始位置向后迭代(start + 1),一个是后往前迭代(end - 1):

<code>override/*IterableLike*/
deffoldRight[B](z:B)(op:(A,B)=>B):B=
foldr(0,length,z,op)
@tailrec
privatedeffoldr[B](start:Int,end:Int,z:B,op:(A,B)=>B):B=
if(start==end)z
elsefoldr(start,end-1,op(this(end-1),z),op)
/<code>

关于这两个还有一个简写就是/: 和:\\ 在scala中以:开头或者结尾的都有特殊的含义的:

<code>valarray=Array(1,2,3,4,5)
valresult=(0/:array)((sum,element)=>sum+element)
println(s"foldLeft=${result}")
/<code>

上面这个是foldLeft的简写,同理 foldRight的简写:

<code>valarray=Array(1,2,3,4,5)
valresult=(array:\\0)((sum,element)=>sum+element)
println(s"foldLeft=${result}")
/<code>

源码如下:

<code>def/:[B](z:B)(op:(B,A)=>B):B=foldLeft(z)(op)
def:\\[B](z:B)(op:(A,B)=>B):B=foldRight(z)(op)
/<code>

其实这个东西 有个名词叫做方法名约定。 我猜 根据冒号的就近原则,调用的是冒号最近的那个实例,所以foldLeft调用的时候array在/:右边,foldRight调用的时候array在;\\左边.

关于下划线: 下划线在scala中很有用,比如在初始化某一个变量的时候了下划线代表的是这个变量的默认的值,
在函数中下划线代表的是占位符,用来表示一个函数值的参数,名字和类型都会被隐晦的指定了,当然如果Scala无法判断下划线代表的类型,那么就可能要报错了。这种情况下当然也可以给下划线指定类型,那就不如用参数名了。

对于上面的foldLeft 你甚至可以这么写:

<code>valarray=Array(1,2,3,4,5)
valresult=(array:\\0)(_+_)
println(s"foldLeft=${result}")
/<code>

看起来是不是简洁的多!


分享到:


相關文章: