Programming languages 闭包中的副作用,它们仍然是纯功能性的吗?

Programming languages 闭包中的副作用,它们仍然是纯功能性的吗?,programming-languages,functional-programming,lisp,Programming Languages,Functional Programming,Lisp,作为函数式编程的新手,我花费了大量精力想知道“这是做事情的函数式方式吗?”显然递归与迭代是非常简单的,而且递归显然是做事情的函数式方式。但以闭包为例。 我已经学习了使用Lisp的闭包,我知道闭包是函数和环境的组合(听起来很像状态和行为)。例如: (let ((x 1)) (defun doubleX() (setf x (* x 2)))) 这里我们有一个函数doubleX,它是在x变量的环境中定义的。我们可以将这个函数传递给其他函数,然后调用

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

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

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

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


也许我就是不懂闭包。有人能帮我吗?

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


您可能会尝试编写一些从外部看起来很实用,但为了提高效率而保持内部可变状态的内容。这方面的一个很好的例子是记忆化,在记忆化中,您可以记录所有以前的调用,以加速像fibonacci这样的函数,但是由于函数总是为相同的输入返回相同的输出,并且不修改任何外部状态,因此可以认为它从外部起作用。

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

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

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


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

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

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

随着时代的到来,需要知道的一点是,任何功能性的东西都是可以并行的,因此避免产生副作用,或者尽可能减少程序的子集是有意义的。

不,它们不是“穷人的对象”,它们更像是一个独生子。