【问题标题】:Side-effects in closures, are they still purely functional?闭包的副作用,它们仍然纯粹是功能性的吗?
【发布时间】:2010-09-22 13:20:00
【问题描述】:

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

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

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

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

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

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

【问题讨论】:

    标签: programming-languages functional-programming lisp


    【解决方案1】:

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

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

    【讨论】:

      【解决方案2】:

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

      When to use closure?

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

      On lambdas, capture, and mutability

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

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

      【讨论】:

      • 不,它们不是“穷人的对象”,它们更像是一个单身人士。
      【解决方案3】:

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

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

      随着时代的到来,需要了解的重要一点是,任何具有功能性的东西都是可并行化的,因此避免产生副作用或在程序中尽可能少的子集是有意义的。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2011-09-05
        • 2014-08-17
        • 2011-01-24
        • 2011-08-14
        • 2017-03-05
        • 1970-01-01
        • 2016-10-28
        • 1970-01-01
        相关资源
        最近更新 更多