封闭中的副作用,它们仍然是纯粹的功能吗?

时间:2008-11-16 06:23:29

标签: programming-languages functional-programming lisp

相对较新的函数式编程,我耗费了大量的精力,想知道“这是做事的功能性方法吗?”显然递归与迭代非常简单,很明显递归是功能性的做事方式。但是以闭包为例。 我已经了解了使用Lisp的闭包,我理解闭包是一个函数和一个环境的组合(听起来很像状态和行为)。例如:

(let ((x 1))
           (defun doubleX()
              (setf x (* x 2))))

这里我们有一个函数doubleX,它已经在x变量的环境中定义。我们可以将此函数传递给其他函数,然后调用它,它仍然可以引用x变量。该函数可以继续引用该变量,即使它是在已定义变量的环境之外调用的。我看过闭包的许多例子都是这样的。其中setf用于更改词法变量的值。这让我感到困惑,因为:

1。)我认为setf是邪恶的。主要是因为它会引起副作用,显然它们也是邪恶的。

2。)这真的“功能性”吗?似乎只是一种保持全球状态的方式,我认为功能语言是无国籍的。

也许我只是不理解闭包。有人可以帮助我吗?

3 个答案:

答案 0 :(得分:12)

你是对的,使用闭包来操纵状态并不是纯粹的功能。 Lisp允许您以功能样式进行编程,但它不会强制您使用。我实际上更喜欢这种方法,因为它允许我在纯功能和修改状态的便利之间取得实用的平衡。

你可能会尝试的是从外部编写看似有用的东西,但保持内部可变状态以提高效率。一个很好的例子就是memoization,你可以记录所有以前的调用来加速像fibonacci这样的函数,但是因为函数总是为同一个输入返回相同的输出并且不修改任何外部状态,所以可以考虑从外面发挥作用。

答案 1 :(得分:8)

闭包是一个穷人的对象(反之亦然),见

When to use closure?

我的回答。因此,如果您打算在非OO应用程序中使用副作用来管理状态,那么关闭over-mutable-state确实是一种简单的方法。不可改变的替代品“不那么邪恶”,但99.9%的语言提供可变状态,并且它们不可能都是错误的。 :)当明智地使用时,可变状态是有价值的,但是当与闭合使用时它可能特别容易出错。捕获,如此处所示

On lambdas, capture, and mutability

在任何情况下,我认为你看到“这么多这样的例子”的原因是解释闭包行为的最常见方法之一是显示一个像这样的小例子,其中一个闭包捕获一个可变的,因此成为一个封装一些可变状态的迷你有状态对象。这是一个很好的例子,可以帮助确保您了解构造的生命周期和副作用,但并不认可在整个地方使用此构造。

大部分时间关闭,你只需关闭值或不可变状态,并且“不注意”你正在做它。

答案 2 :(得分:2)

Common Lisp和Scheme不是纯粹的功能。 Clojure主要是功能性的,但仍然不是纯粹的。 Haskell是我所知道的唯一纯粹功能性的语言,我甚至不能提到另一种语言的名称。

事实是,在纯粹的功能环境中工作非常困难(去学习Haskell并尝试在其上编程)。所以所有这些函数式编程语言真正做的是允许函数式编程,但不强制执行它。功能编程功能非常强大,因此请尽可能地使用它,何时不能使用。

重要的是要了解即将到来的年龄,任何有功能的东西都是可以兼容的,因此避免产生副作用或尽可能使用最小的程序子集是有意义的。

相关问题