匿名函数
(x: Double) => 3 * x
可以看到,函数就是接收输入,执行算法,返回结果。由于输入都是参数,所以函数其实是无状态的。
我们可以把这个函数存放在变量中:
val triple = (x: Double) => 3 * x
这就跟你用def一样:
def triple = (x: Double) => 3 * x
存放变量中就是命名函数。
匿名函数和命名函数都是对象,可以作为参数输入,也可以作为结果返回。
有状态的函数
上面的例子中,我们定义了一个算法(函数),它的作用是将输入参数乘以3。加入我们想让它变得更通用一些,可以对输入参数乘以y,也就是说,我们要定义这么一个函数:
(x: Double) => x * y
我们可以把它命名为multipy:
def multiply = (x: Double) => x * y
x是输入参数,那么y呢?目前看来只能是全局变量了:
var y = 1
def multiply = (x: Double) => x * y
Array(3.14, 1.42, 2.0).map(multiply)
y = 2
Array(2.14, 4.32, 2.54).map(multiply)
虽然引入了状态,带来了一些灵活性,但是由于是全局变量,并没有带来任何好处。还不如引入一个参数:
def multiply = (x: Double, y: Double) => x * y
这也是大部分命令式编程语言提供的做法。如果能够像对象一样,有自己的私有数据(状态)就好了,也就是说:
class Multiply(factor: Double){
def apply = (x: Double) => x * factor;
}
然后我们就可以这样子使用了:
val triple = new Multiply(3)
val half = new Multiple(0.5)
triple.apply(14) // 42
half.apply(14) // 7
如果还像C++提供了括号操作符重载机制,那么可以看起来就像函数调用一样:
triple(14) // 42
half(14) // 7
看起来不错,其实我们就是创建了一个有状态的对象,然后调用这个对象的某个方法。在函数式语言中,函数不就是对象吗?! 函数不是可以创建另外一个函数吗?! 函数不是可以作为返回值返回吗?! 关键是函数能够拥有状态吗?答案是可以的。
创建函数的函数
def multiply(factor: Double) = (x: Double) => factor * x
val triple = multiply(3)
val half = multiply(0.5)
triple(14)
half(14)
这里的关键在于,返回的函数还能访问到创建它的multiply函数的参数factor。正常情况下,multiple(3)执行完成后,factor作为multiply的局部变量,应该是被销毁了。但是,在FP中,由于有函数引用到它,所以这个变量的生命周期被延迟了,被函数封闭起来的变量的寿命,与封闭它的函数对象寿命相等。也就是说,当封闭这个变量的函数对象不再被访问,被垃圾回收器回收掉时,这个变量的寿命也就同时终结了。这就是闭包,它有两个强大的副作用:
- 作用域:解决全局变量问题
- 生命周期:解决局部变量调用后不可见问题
局部变量可以是外层函数(构造函数)的局部变量:
function extent(){
var n = 0; // 局部变量,而非参数
return function(){
n++;
console.log("n=" + n);
}
}
f = extent();
f(); // n=1
f(); // n=2