1. 基础
初学程序设计时,比较容易混淆的两个概念是数学函数(math function)和程序中使用的函数。
在数学函数中 y=f(x)y=f(x),一个输入值有固定的输出值。例如,无论计算多少次,sinπsinπ 的结果总是 0。如果 f(x)=x/2f(x)=x/2,那么 f(10)f(10) 无论计算 100 次还是 1000 次,其结果都是 5.
程序设计中的函数却不具备这种稳定的特性,因为函数的执行不仅依赖于输入值,而且会受到全局变量,输入文件,类的成员变量等诸多因素的影响。如下:
int counter = 0;
int count(){
return ++counter;
}
此函数输入没有输入值,但每次都返回不同的结果。当然,就像数学函数那样,程序中函数还可以设计成“对同一输入值每次都返回相同结果”的形式。
函数的返回值只依赖于其输入值,这种特性就称为引用透明性(referential transparency)
2. 动态规划的缓存
显然,动态规划所使用的制表法(也即缓存)只能应用于具有引用透明性的函数。如果外在因素使相同输入值返回不同结果值,则不能缓存。
也即缓存对应的 map,实现的是同一个输入(key),同一个输出(value),而不可能出现同一个输入,可以得到不同的输出,也即输出结果的不确定性。
3 Scala纯函数
对于scala的并行容器 所使用的方法中(运算子),只要它们的运算子是纯函数,那么结果将是确定的。
纯函数指的是给定相同输入总是得到相同输出的函数,即没有任何副作用。
比如,函数(x: Int) => x + 1是纯函数。而下面的函数则不是纯函数,因为它修改了uid值的状态。
val uid = new AtomicInteger(0)
val f = (x: Int) => (x, uid.incrementAndGet())
即使一个函数没有修改任何内存位置,如果它读取的内存位置有可能发生变化,那它也不是纯函数。比如,下面的函数g就不是纯函数。
val g = (x: Int) => (x, uid.get)