在FP里面很重要的三个理念:闭包,柯里化,偏函数;用Scala代码简单演示一下

Closures

wiki定义:闭包是由函数和与其相关的引用环境组合而成的实体.

字面意思不是很好理解,看下面这个例子

package otherscope {
class Foo {
// a method that takes a function and a string, and passes the string into
// the function, and then executes the function
def exec(f:(String) => Unit, name: String) {
f(name)
}
}
}

object ClosureExample extends App {
var hello = "Hello"
def sayHello(name: String) { println(s"$hello, $name") }

// execute sayHello from the exec method foo
val foo = new otherscope.Foo
foo.exec(sayHello, "Al")

// change the local variable 'hello', then execute sayHello from
// the exec method of foo, and see what happens
hello = "Hola"
foo.exec(sayHello, "Lorenzo")
}

// Result:
// Hello, Al
// Hola, Lorenzo

你会发现,定义sayHello()的时候,没有入参hello! 但是呢,环顾四周还是有定义的,重要的是不仅第一次exec调用时获取到值,第二次调用竟会神奇的感知到hello的变更!

在闭包概念里,hello这种类型的变量称为free variable,看名字就不是一个简单变量,实际上它是一个引用,或者叫指针,所以才能感知外围变量值的变化

形象的两个比喻:文艺版,理工癌版

  • 这就像两个恋人至死不渝,由于某些原因被分割天涯,但是爱情的魔力使他俩仍能感知彼此;这两个恋人就是那个闭包函数和那个free variable
  • 量子纠缠: 粒子在由两个或两个以上粒子组成系统中相互影响的现象,虽然粒子在空间上可能分开. 爱因斯坦将量子纠缠称为“鬼魅似的远距作用”(spooky action at a distance)

同时也能看到FP里面实现闭包很简洁,因为天生支持高阶函数

Currying

wiki定义:是把接受多个参数的函数变换成接受一个单一参数(最初函数的第一个参数)的函数,并且返回接受余下的参数而且返回结果的新函数的技术.

还是不直观,来看Scala例子

// Type: (Int,Int) => Int
def plus1(x : Int, y : Int) = {x + y} // (9, 1) => {9 + 1}
// Type: Int => Int => Int
def plus2(x : Int)(y : Int) = {x + y} // (9)(1) => {9 + y} => {9 + 1}
val p9 = plus2(9)_
p9(1) // Result: 10

你会发现plus2函数类型多了个=>,这个符号表示转换,也就表明多了次转换;val p9 = plus2(9)_ => plus2(9)(y) => {9 + y}; 所以p9(1) => 9 + 1 => 10

回头再看定义是不是就明白了,Currying就是你给了部分参数,然后会对部分入参进行运算(能算多少算多少)得到一个新函数,并且这个新函数的入参就是你没给的那些参数,这个新函数就叫curried function

Partials

wiki定义:a partial function from X to Y (written as f: X ↛ Y) is a function f: X ′ → Y, for some subset X ′ of X
说白了就是一个全函数(接受所有入参集合),但是只想拆分出来几个只能处理部分参数集合的子集函数

def minus(x: Int, y: Int) = {x - y}
val p9first = minus(9, _: Int)
p9first(1) // Result: 8
val p9second = minus(_: Int, 9)
p9second(1) // Result: -8

比如minus是全函数,可以接收任何可能的(x,y)作为入参,但是我只想考虑(x=9,y=any)的入参集合,或者(x=any,y=9)的入参集合;可能这个不是很标准,Scala的标准做法是使用PartialFunction,感兴趣的话可以参考Reference

Reference