Scala函数的柯里化

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

Scala的匿名函数:

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

val inc = (x:Int) => x + 1
scala> inc(7)
res10: Int = 8

也可以不设置参数列表:

scala> val out = () => print(10)
scala> out()
10

Scala函数的柯里化:

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

 def add(x:Int, y:Int)=x+y

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

def add(x:Int)(y:Int)=x+y

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

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

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

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

def add(x:Int) = (y:Int) => x + y

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

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

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

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

scala> def add(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

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

override /*TraversableLike*/
def foldLeft[B](z: B)(op: (B, A) => B): B =
  foldl(0, length, z, op)

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

@tailrec
private def foldl[B](start: Int, end: Int, z: B, op: (B, A) => B): B =
  if (start == end) z
  else foldl(start + 1, end, op(z, this(start)), op)

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

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

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

override /*IterableLike*/
def foldRight[B](z: B)(op: (A, B) => B): B =
  foldr(0, length, z, op)
  @tailrec
private def foldr[B](start: Int, end: Int, z: B, op: (A, B) => B): B =
  if (start == end) z
  else foldr(start, end - 1, op(this(end - 1), z), op)

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

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

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

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

源码如下:

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

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

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

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

val array = Array(1, 2, 3, 4, 5)
val result = (array:\0)(_+_)
println(s"foldLeft = ${result}")

看起来是不是简洁的多!

在这里插入图片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章