2017-07-19 101 views
4

我一直在阅读函数式编程及其概念。我很清楚,在大型项目中工作时,您总是需要混合(在某种适当的层面上)多种范例,例如面向对象和功能。理论上,函数纯度等概念太严格,例如:这可以被认为是一个纯函数(函数式编程)吗?

该函数总是在给定相同参数值的情况下评估相同的结果值。函数结果值不能取决于程序执行过程中或程序的不同执行之间可能改变的任何隐藏信息或状态,也不能取决于来自I/O设备的任何外部输入。 (https://en.wikipedia.org/wiki/Pure_function

这就是说,这是(或可以考虑)代码一个纯函数?

const externalVar = 10; 

function timesTen(value) { 
    return externalVar * value; 
} 

我问这是因为,在这种情况下,timesTen功能将总是用于输入返回相同的值,任何人都可以改变的externalVar值,因为这是一个常数。但是,这段代码打破了访问外部函数范围的规则。

+2

请注意,纯函数也经常与其他函数有依赖关系,只要这些其他函数是纯的,依赖函数也被认为是纯粹的。我看不出你的例子与此不同。所以是的,'timesTen'是一个纯粹的功能。尽管你不能保证引用透明度。因此,说你可以确信你的功能是纯粹的,这可能会更合适。 – ftor

+0

我完全同意你@ftor!就语言而言,请忽略它。我的问题是关于概念,而不是实际上用某种特定的语言可以实现什么概念。 – thiagoh

+1

@thiagoh当然,如果它的所有依赖都是纯粹的,那么函数仍然是纯粹的 - 这是ftor所说的内容,我同意他100%。 – naomik

回答

4

是的。它保证是纯净的。

原因是它只依赖于绑定和不可变的自由变量。

但是,这段代码打破了访问外部函数的范围的规则,即 范围。

你的报价中没有什么说你不能访问自由变量。它说外部输入是从文件,网络等中读取的,而不是来自先前范​​围的自由变量。

即使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。它看起来很纯,但可能会被污染。如果传递给它的两个函数都不是纯粹的,结果也不是。

OO也可以是纯粹的功能!

通过不改变您创建的对象可以轻松地组合它们。

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,你会说这不是纯粹的。这与someMethodobj也是如此。

您可以制作适用于大型数据结构的功能类,这意味着您在进行回溯拼图解算器时无需在更改之前将其复制。一个简单的例子是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); 

lst0lst但与前面的新元素。 lst0重复使用lst中的所有内容。从列表到二叉树的所有内容都可以用这种方法制作,并且可以使用不可变的二叉树制作许多连续的数据结构。它从50年代开始就一直存在。

+0

“如果您将parseInt重新定义为不纯或在程序执行过程中发生变化,以便其工作方式不同,则不会有任何调用它的函数是纯粹的。”这正是我的想法!经过与朋友的多次讨论,我在这里提出了这个问题他们被那些说纯功能无法从外部访问的字面单词所蒙蔽。接受这是正确的答案。谢谢 – thiagoh

1

我理解你的意见并完全同意@Sylwester,但有一点值得一提:用反射外部常量值可以修改并打破你函数的纯粹性。我们知道,IT中的所有内容都可以被黑客入侵,我们不应该将这个概念考虑在内,但是在实践中我们应该清楚地记住这一点,以这种方式来说,功能纯粹是不健全的。

相关问题