Scala基础(3)- 函数进阶
程序员文章站
2023-12-30 22:05:22
...
匿名函数
函数可以没有名称,可以直接赋值。如果函数有多行表达式组成,就加上大括号。这一点对匿名函数同样适用。
scala> val addOne = (x: Int) => x + 1
addOne: (Int) => Int = <function1>
scala> addOne(1)
res4: Int = 2
注意到Scala中一切都是对象。所以addOne
是一个对象。同时它又是个包含一个参数的函数。此时,函数和对象得到了统一。
偏函数应用 (Partial Application)
偏函数应用解决这样的问题:如果我们有函数是多个参数的,我们希望能固定其中某几个参数的值。在Scala中,我们可以这么做
scala> def adder(m: Int, n: Int) = m + n
adder: (m: Int,n: Int)Int
scala> val add2 = adder(2, _:Int)
add2: (Int) => Int = <function1>
scala> add2(3)
res50: Int = 5
函数柯里化(Currying)
偏函数应用不是什么新东西,几乎所有编程语言中都可以简单地实现。但更简洁的实现是柯里化。
你可以这样定义柯里化 函数
scala> def multiply(m: Int)(n: Int): Int = m * n
multiply: (m: Int)(n: Int)Int
也可以对一个已有的函数进行转换。例如之前的adder函数
scala> (adder _).curried
res1: (Int) => (Int) => Int = <function1>
函数柯里化依赖匿名函数,把一个多参数的函数转化为多个单参数的函数连接在一起。这样一来,语意的表达就更加丰富而直观,函数组合也更加灵活。
类型推断
一个应用场景就是类型推断。Scala类库中的foldLeft
就是这样一个例子。
def foldLeft[B](z: B)(op: (B, A) => B): B
List("").foldLeft(0)(_ + _.length)
如果使用普通的多参数函数定义
def foldLeft[B](z: B, op: (B, A) => B): B
那么,你在使用时就必须显示地表明类型。
List("").foldLeft(0, (b: Int, a: String) => a + b.length)
List("").foldLeft[Int](0, _ + _.length)
简化API
另一个currying的应用是简化API。在下面的例子中,你可以使用大括号来传递body
,使得API调用层次分明。
def loop[A](n: Int)(body: => A): Unit = (0 until n) foreach (n => body)
loop(2) {
println("hello!")
}
多阶段计算
如果你的多参数函数的计算实际上是分步骤的,某个步骤只依赖某个参数,比如下面的例子。那么柯里化会更加简单。
def v(t: Double, k: Double): Double = {
// 只依赖于t
val ft = f(t)
g(ft, k)
}
v(1, 1); v(1, 2);