15

作为函数式编程的新手,我花了很多精力想“这是函数式的做事方式吗?” 显然,递归与迭代非常简单,很明显递归是做事的功能性方式。但以闭包为例。我已经了解了使用 Lisp 的闭包,并且我了解闭包是函数和环境的组合(听起来很像状态和行为)。例如:

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

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

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

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

也许我只是不明白闭包。有人可以帮我吗?

4

3 回答 3

13

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

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

于 2008-11-16T06:39:28.033 回答
8

闭包是穷人的对象(反之亦然),请参阅

什么时候使用闭包?

以及我在其中的回答。因此,如果您打算使用副作用来管理非 OO 应用程序中的状态,则闭包可变状态确实是一种简单的方法。不可变的替代方案“不那么邪恶”,但 99.9% 的语言提供可变状态,而且它们不可能都是错误的。:) 明智地使用可变状态是很有价值的,但是当与闭包和捕获一起使用时,它尤其容易出错,如此处所示

关于 lambdas、捕获和可变性

无论如何,我认为你看到“这么多这样的例子”的原因是解释闭包行为的最常见方法之一是展示一个像这样的小例子,其中闭包捕获一个可变的,因此变成一个迷你-stateful-object 封装了一些可变状态。这是一个很好的示例,可以帮助确保您了解该构造的生命周期和副作用影响,但这并不是在各处使用该构造的认可。

大多数情况下,使用闭包,您只会关闭值或不可变状态,而“没有注意到”您正在这样做。

于 2008-11-16T06:41:41.977 回答
3

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

事实是,在纯函数式环境中工作非常困难(去学习 Haskell 并尝试在上面编写一些东西)。所以所有这些函数式编程语言实际上他们所做的是允许函数式编程,但不强制执行它。函数式编程非常强大,所以在可以使用的时候使用它,在不能使用的时候使用它。

随着时代的到来,重要的是要知道任何功能都是可并行的,因此避免产生副作用或在程序的尽可能小的子集中是有意义的。

于 2008-11-26T14:20:30.970 回答