我一直在阅读有关函数式编程及其概念的内容。我很清楚,在大项目中工作时,你总是需要混合(在某种程度上)多种范例,如OO和功能。从理论上讲,功能纯度等概念过于严格,例如
在给定相同参数值的情况下,函数始终评估相同的结果值。功能结果值不能取决于程序执行过程中或程序的不同执行之间可能发生变化的任何隐藏信息或状态,也不依赖于I / O设备的任何外部输入。 (https://en.wikipedia.org/wiki/Pure_function)
那说,这个(或者可以考虑)代码是纯函数吗?
const externalVar = 10;
function timesTen(value) {
return externalVar * value;
}
我问这是因为,在这种情况下,timesTen
函数始终会为输入返回相同的值,任何人都可以更改externalVar
的值因为这是一个常数。但是,此代码违反了访问外部函数范围的规则。
答案 0 :(得分:5)
是。它保证是纯净的。
原因是它只依赖于绑定和不可变的自由变量。
但是,此代码违反了访问外部函数的规则 范围。
您的引言中没有任何内容表明您无法访问自由变量。它表示外部输入是从文件,网络等读取而不是来自先前范围的自由变量。
即使是Haskell也使用像foldr
这样的全局函数名称,它在每个函数中都是一个自由变量,当然结果是纯粹的。
请记住,按名称排列的函数只是变量。 parseInt
是一个指向函数的变量,因此如果要在另一个函数中使用的每个函数都作为参数传递,那么根本不需要做任何事情。
如果您将parseInt
重新定义为非纯粹的东西或在程序的持续时间内,以便它以不同的方式工作,那么调用它的函数就不会是纯粹的。
功能组合和部分评估工作,因为它们提供自由变量。它是函数式编程中一种必不可少的抽象方法。例如
function compose(f2, f1) {
return (...args) => f2(f1(...args));
}
function makeAdder(initialValue) {
return v => v + initialValue;
}
const add11 = compose(makeAdder(10), makeAdder(1));
add11(5); // ==> 16
这很纯粹。闭包变量/自由变量f1
,f2
,initialValue
永远不会对创建的函数进行更改。 add11
是一个纯函数。
现在再看一下compose
。它看起来很纯净但可能会受到污染。如果不是两个函数都传递给它是纯粹的,结果也不是。
通过不改变您创建的对象,可以轻松地将它们组合在一起。
class FunctionalNumber {
constructor(value) {
this.value = value;
}
add(fn) {
return new FunctionalNumber(this.value + fn.value);
}
sub(fn) {
return new FunctionalNumber(this.value - fn.value);
}
}
这个课程纯粹是功能性的。
实际上,您可以将obj.someMethod(arg1, arg2)
之类的方法调用视为以obj
作为第一个参数someFunction(obj, arg1, arg2)
的函数调用。它只是语法差异,如果someFunction
变异obj
你会说它不纯粹。这与someMethod
和obj
的关系也是如此。
您可以创建适用于大型数据结构的类,这意味着您在进行回溯拼图求解器更改之前无需复制它。一个简单的例子是Haskell和Lisp中的对。这是在JavaScript中实现它的一种方法:
class Cons {
constructor(car, cdr) {
this.car = car;
this.cdr = cdr;
}
}
const lst = new Cons(1, new Cons(2, new Cons(3, null)));
const lst0 = new Cons(0, lst);
lst0
是lst
,但前面有一个新元素。 lst0
重用lst
中的所有内容。从列表到二叉树的所有内容都可以用这个来创建,你可以用不可变的二叉树创建许多顺序数据结构。它自50年代以来一直存在。
答案 1 :(得分:1)
我理解你的意见并完全赞同@Sylwester,但有一点值得一提:反射外部常数值可以修改并打破你的功能的纯粹性。我们知道IT中的所有内容都可以被黑客攻击,我们不应该考虑这些概念,但在实践中我们应该清楚地记住这一点,这样功能的纯粹性是不合理的。