匿名函数

(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中,由于有函数引用到它,所以这个变量的生命周期被延迟了,被函数封闭起来的变量的寿命,与封闭它的函数对象寿命相等。也就是说,当封闭这个变量的函数对象不再被访问,被垃圾回收器回收掉时,这个变量的寿命也就同时终结了。这就是闭包,它有两个强大的副作用:

  1. 作用域:解决全局变量问题
  2. 生命周期:解决局部变量调用后不可见问题

局部变量可以是外层函数(构造函数)的局部变量:

function extent(){
	var n = 0; // 局部变量,而非参数
	return function(){
		n++;
		console.log("n=" + n);
	}
}

f = extent();
f(); // n=1
f(); // n=2