Scala 函数式编程
说到函数式编程,那么就不得不提到几个概念
- 函数副作用
- 纯函数
- 非纯函数
- 参照透明性
副作用
在计算机科学中,函数副作用指当调用函数时,除了返回可能的函数值之外,还对主调用函数产生附加的影响。例如修改全局变量(函数外的变量),修改参数,向主调方的终端、管道输出字符或改变外部存储信息等。
在某些情况下函数副作用会给程序设计带来不必要的麻烦,给程序带来十分难以查找的错误,并降低程序的可读性与可移植性。严格的函数式语言要求函数必须无任何副作用,但功能性静态函数本身的目的正是产生某些副作用。在生命科学中,副作用往往带有贬义,但在计算机科学中,副作用有时正是“主要作用”。
所以,我们应当尽量避免函数副作用,当然在某些情况下,副作用无法避免,比如异常处理等
纯函数
纯函数(英语:Pure Function)——输入输出数据流全是显式(英语:Explicit)的。
显式的意思是
- 函数与外界交换数据只有一个唯一渠道——参数和返回值;
- 函数从函数外部接受的所有输入信息都通过参数传递到该函数内部;
- 函数输出到函数外部的所有信息都通过返回值传递到该函数外部。
非纯函数
如果一个函数通过隐式(英语:Implicit)方式,从外界获取数据,或者向外部输出数据,那么,该函数就不是纯函数,叫作非纯函数(英语:Impure Function)。
隐式的意思是
函数通过参数和返回值以外的渠道,和外界进行数据交换。
比如,读取全局变量,修改全局变量,都叫作以隐式的方式和外界进行数据交换;比如,利用 I/O API(输入输出系统函数库)读取配置文件,或者输出到文件,打印到屏幕,都叫做隐式的方式和外界进行数据交换。
特殊的非纯函数
上述所表达的,是对于函数外部变量的更改,但是需要注意的是,从严格意义上来说,对函数入参做出的更改也算是一种非纯函数
伪代码如下所示
process(context) {
a = context.getInfo()
result = calculate(a)
context.setResult(result)
}
参照透明性
无副作用是参照透明性(英语:Referential transparency)(英语:Referential Transparent)的必要非充分条件。
参照透明意味着一个表达式(例如一次函数调用)可以被替换为它的值。这需要该表达式是纯的,也就是说该表达式必须是完全确定的(相同的输入总是导致相同的输出)而且没有副作用。
样例
f(x) {
return x + 1
}
f(x)函数就是纯函数。
a = 0
q(x) {
b = a
}
q(x)访问了函数外部的变量。q(x)是非纯函数。
p(x) {
print“hello”
}
p(x)通过I/O API输出了一个字符串。p(x)是非纯函数。
c(x) {
// 假设readConfig()函数为I/O API的函數
// 读取配置文件
data = readConfig()
}
c(x)通过I/O API读取了配置文件。c(x)是非纯函数。
函数内部有隐式(Implicit)的数据流,这种情况叫做副作用(Side Effect)。上述的I/O,外部变量等,都可以归为副作用。因此,纯函数的定义也可以写为“没有副作用的函数”。
I/O API 可以看作是一种特殊的全局变量。文件、屏幕、数据库等输入输出结构可以看作是独立于运行环境之外的系统外全局变量,而不是应用程序自己定义的全局变量。