纯函数、反作用和引用透明性
了解函数式编程的人可能据说过纯函数和反作用等名称。这两个概念与引用透明性紧密相关。纯函数须要具有两个特征:
1、对于相同的输入参数,老是返回相同的值。
2、求值过程当中不产生反作用,也就是不会对运行环境产生影响。
对于第一个特征,若是是从数学概念上抽象出来的函数,则很容易理解。好比f(x)=x+1和g(x)=x*x这样的函数,都是典型的纯函数。若是考虑到通常编程语言中出现的方法,则函数中不能使用静态局部变量、非局部变量,可变对象的引用或I/O流。这是由于这些变量的值可能在不一样的函数执行中发生变化,致使产生不同的输出。
第二个特征,要求函数体中不能对静态局部变量、非局部变量,可变对象的引用或I/O流进行修改。这就保证了函数的执行过程当中不会对外部环境形成影响。纯函数的这两个特征缺一不可。下面经过几个Java方法来具体说明纯函数。
在清单1中,方法f1是纯函数;方法f2不是纯函数,由于引用了外部变量y;方法f3不是纯函数,由于使用了调用了产生反作用的Counter对象的inc方法;方法f4不是纯函数,由于调用writeFile方法会写入文件,从而对外部环境形成影响。
清单1.纯函数和非纯函数示例
int f1(int x) {
return x + 1;}
int f2(int x) {
return x + y;}
int f3(Counter c) {
c.inc();
return 0;}
int f4(int x) {
writeFile();
return 1;}
若是一个表达式是纯的,那么它在代码中的全部出现,均可以用它的值来代替。对于一个函数调用来讲,这就意味着,对于同一个函数的输入参数相同的调用,均可以用其值来代替。这就是函数的引用透明性。引用透明性的重要性在于使得编译器能够用各类措施来对代码进行自动优化。
函数式编程与并发编程
随着计算机硬件多核的普及,为了尽量地利用硬件平台的能力,并发编程显得尤其重要。与传统的命令式编程范式相比,函数式编程范式因为其自然的无状态特性,在并发编程中有着独特的优点。
以Java平台来讲,相信不少开发人员都对Java的多线程和并发编程有所了解。可能最直观的感觉是,Java平台的多线程和并发编程并不容易掌握。这主要是由于其中所涉及的概念太多,从Java内存模型,到底层原语synchronized和wait/notify,再到java.util.concurrent包中的高级同步对象。
因为并发编程的复杂性,即便是经验丰富的开发人员,也很难保证多线程代码不出现错误。不少错误只在运行时的特定状况下出现,很难排错和修复。在学习如何更好的进行并发编程的同时,咱们能够从另一个角度来看待这个问题。
多线程编程的问题根源在于对共享变量的并发访问。若是这样的访问并不须要存在,那么天然就不存在多线程相关的问题。在函数式编程范式中,函数中并不存在可变的状态,也就不须要对它们的访问进行控制。这就从根本上避免了多线程的问题。