首页 | 新闻 | 新品 | 文库 | 方案 | 视频 | 下载 | 商城 | 开发板 | 数据中心 | 座谈新版 | 培训 | 工具 | 博客 | 论坛 | 百科 | GEC | 活动 | 主题月 | 电子展
返回列表 回复 发帖

函数式编程思想概论(4)纯函数、副作用和引用透明性

函数式编程思想概论(4)纯函数、副作用和引用透明性

纯函数、副作用和引用透明性了解函数式编程的人可能听说过纯函数和副作用等名称。这两个概念与引用透明性紧密相关。纯函数需要具备两个特征:
  • 对于相同的输入参数,总是返回相同的值。
  • 求值过程中不产生副作用,也就是不会对运行环境产生影响。
对于第一个特征,如果是从数学概念上抽象出来的函数,则很容易理解。比如 f(x)=x+1 和 g(x)=x*x        这样的函数,都是典型的纯函数。如果考虑到一般编程语言中出现的方法,则函数中不能使用静态局部变量、非局部变量,可变对象的引用或 I/O        流。这是因为这些变量的值可能在不同的函数执行中发生变化,导致产生不一样的输出。第二个特征,要求函数体中不能对静态局部变量、非局部变量,可变对象的引用或 I/O        流进行修改。这就保证了函数的执行过程中不会对外部环境造成影响。纯函数的这两个特征缺一不可。下面通过几个 Java 方法来具体说明纯函数。
在清单 1 中,方法 f1 是纯函数;方法 f2 不是纯函数,因为引用了外部变量 y;方法 f3 不是纯函数,因为使用了调用了产生副作用的 Counter 对象的 inc 方法;方法        f4 不是纯函数,因为调用 writeFile 方法会写入文件,从而对外部环境造成影响。
清单 1.        纯函数和非纯函数示例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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        包中的高级同步对象。由于并发编程的复杂性,即使是经验丰富的开发人员,也很难保证多线程代码不出现错误。很多错误只在运行时的特定情况下出现,很难排错和修复。在学习如何更好的进行并发编程的同时,我们可以从另外一个角度来看待这个问题。多线程编程的问题根源在于对共享变量的并发访问。如果这样的访问并不需要存在,那么自然就不存在多线程相关的问题。在函数式编程范式中,函数中并不存在可变的状态,也就不需要对它们的访问进行控制。这就从根本上避免了多线程的问题。
返回列表