一个函数在程序执行的过程中除了根据输入参数给出运算结果之外没有其他的副作用影响,我们可以把这类函数称为“纯函数”。纯函数由于不依赖外部变量,使得给定函数输入其返回结果永远不变,比如整数的加法函数,它接收两个整数值并返回一个整数值,对于给定的两个整数值,它的返回值永远是相同的整数值。
相对于非纯函数,它们带有副作用,这使得函数不仅简单返回一个值,还做了其他事情:
像是执行IO、处理错误、修改数据都属于副作用,这些副作用可能导致我们编写的程序难以测试,进而容易产生bug。遵循函数式编程规范可以使编程更加模块化,由于纯函数模块化的特性,使得程序 容易被测试、复用、并行化、泛化以及推导 ,这也减少了产生bug的可能。
纯函数的使用将数据的创建过程和处理过程分离,通过把副作用推导程序的外层,来转换任何带有副作用的函数。对于函数式程序员而言,程序的实现应该有一个纯的内核和一层很薄的外围来处理副作用。
引用透明(referential transparency)的概念对纯函数进行形式化,符合引用透明的表达式都可以由它的结果所取代,而不改变该程序的含义。当调用一个函数时所传入的参数是引用透明的,冰鞋函数调用也是引用透明的,那么这个函数式一个纯函数。当传入函数的参数也是一个纯函数时,使得高阶函数的组合也是引用透明的,这有利于构建更加复杂的逻辑,而本身程序的计算结果是可以进行推断的,不用担心环境对程序的影响。
对于程序p,如果它包含的表达式e满足引用透明,所有的e都可以替换为它的运算结果而不会改变程序p的含义。假设存在一个函数f,若变大时f(x)对所有引用透明的表达式x也是引用透明的,那么这个f是一个纯函数。
引用透明要求函数不论进行了任何操作都可以用它的返回值来代替。这种限制使得推导一个程序的求值变得简单而自然,称之为替代模型(substitution model)。如果表达式是引用透明的,可以想象计算过程就像在解代数方程。展开表达式的每一部分,使用指示对象替代变量,然后归约到最简单的形式。在这一过程中,每项都被等价值所替代,计算的过程就是一个又一个等价值所替代的过程。换句话说,引用透明使得程序具备了等式推理的能力。
替代模型更容易推理,因为对运算的影响纯粹是局部的(只对那些赋值表达式产生影响),不需要先在内心模拟一系列状态的更新才理解这一段代码。只需要理解局部的推理,不必费心地去跟踪函数执行前后的状态变化,只用简单看一下函数的定义,把它替换成一个参数。
这一小节,我们了解了纯函数的一些基本概念和其带来的好处。我们知道,纯函数时模块化的、可组合的,因为它从“对结果做什么”和“如果获取输入”中分离了计算本身的逻辑,就像一个黑盒子。对输入的获取只有一种方式:通过参数传给函数。输出也只是简单地将计算结果返回。把这些关注点分离开,计算也更容易被复用。我们可以复用这些逻辑,而不必担心输入或输出对整个上下文引起的副作用。
转载请注明作者Jason Ding及其出处
jasonding.top
Github博客主页(http://blog.jasonding.top/)
CSDN博客(http://blog.csdn.net/jasonding1354)
简书主页(http://www.jianshu.com/users/2bd9b48f6ea8/latest_articles)
Google搜索jasonding1354进入我的博客主页