functional-programming - 什么时候可以修改函数式语言中的变量?

标签 functional-programming scheme lisp purely-functional mutability

所以我正在使用 Racket Scheme 自学函数式编程,到目前为止我很喜欢它。作为我自己的练习,我一直在尝试以纯函数式的方式实现一些简单的任务。我知道不变性是函数式风格的重要组成部分,但我想知道是否有任何时候它是可以的。

我想到了一种有趣的方法,让函数在与过滤器一起使用时从字符串列表中去除非唯一字符串,如下所示:

(define (make-uniquer)
  (let ([uniques '()])
    (lambda (x)
      (if (not (member x uniques))
          (set! uniques (cons x uniques))
          #f))))

(define (uniquify x)
  (let ([uniquer (make-uniquer)])
    (filter uniquer x)))

如您所见,make-uniquer 返回一个字符串列表的闭包以比较其唯一性,这样它就可以充当过滤器的简单谓词。但我正在破坏性地更新关闭列表。这是一种错误的形式,还是以这种方式更改局部封闭变量是否可以?

最佳答案

纯和非纯函数式编程

Pure function s 本质上是 referentially transparent ,它允许内存(缓存结果)。缺少可变状态允许重入,允许不同版本的链接数据结构共享内存,并使自动并行化成为可能。关键是通过限制自己改变状态,您不再需要考虑命令式编程的许多复杂问题。

但是,这种限制也有缺点。一是性能:某些算法和数据结构(如构建哈希表)如果不复制大量数据就无法表示为纯函数。另:对比Haskell这种纯函数式语言。由于突变不存在(概念上),您必须使用 monad 表示状态变化秒。 (虽然 Haskell 提供了一个相当简洁的 do-notation 语法糖,但在状态 monad 中编程是与“常规”Haskell 完全不同的语言!)如果您的算法最容易使用几个互锁循环来表达状态,Haskell 实现将比不纯语言的实现更笨重。

一个示例是更改深深嵌套在 XML 文档中的单个节点。使用 zipper data structures 在没有状态突变的情况下是可能的但更困难.示例伪代码(纯):

old_xml = <a><b><c><d><e><f><g><h attrib="oldvalue"/></g></f></e></d></c></b></a>
// '\' is the XML selection operator
node_to_change = orig_xml \ "a" \ "b" \ "c" \ "d" \ "e" \ "f" \ "g" \ "h"
node_changed = node_to_change.copy("attrib" -> "newvalue")
new_xml = node_changed.unselect().unselect().unselect().unselect()
                      .unselect().unselect().unselect().unselect()
return new_xml

示例(不纯):

xml = <a><b><c><d><e><f><g><h attrib="oldvalue"/></g></f></e></d></c></b></a>
node_to_change = orig_xml.select_by_xpath("/a/b/c/d/e/f/g/h")
node_to_change.set("attrib" -> "newvalue")
return xml    // xml has already been updated

有关纯函数数据结构的更多信息,请参阅 https://cstheory.stackexchange.com/questions/1539/whats-new-in-purely-functional-data-structures-since-okasaki . (此外,通常可以编写一个只操作内部状态的过程函数,这样它就可以被包装起来,这样对于调用者来说它实际上是一个纯函数。这在非纯语言中要容易一些,因为你不需要必须将其写入状态 monad 并将其传递给 runST。)

虽然以不纯的风格编写你失去了这些好处,但函数式编程的一些其他便利(如高阶函数)仍然适用。

使用突变

Lisp 是一个 impure函数式语言,这意味着它允许状态突变。 这是设计使然,因此如果您需要更改,您可以使用它而无需使用其他语言。

一般来说,,当

  • 出于性能原因需要它,或者
  • 您的算法可以使用变异更清楚地表达。

当你这样做时:

  • 明确记录您的 uniquify 函数将改变您传递给它的列表。调用者将一个变量传递给您的函数并让它返回更改是很讨厌的!
  • 如果您的应用程序是多线程的,请分析、注意并记录您的非纯函数是否是线程安全的。

关于functional-programming - 什么时候可以修改函数式语言中的变量?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/12046955/

相关文章:

lisp - 如何将 "or"应用于 elisp 中的列表

lisp - Common Lisp中有没有像Python的 `if __name__ == "__main_ _":`之类的东西

functional-programming - 生成真值表的更简单方法

algorithm - 对数时间方案算法

javascript - 如何在函数式 JavaScript 中存储数组的状态?

scheme - 为什么要返回这个?

函数的方案结果

functional-programming - Scheme中处理用户输入的程序代码设计

haskell - 如何比较两个函数的等价性,如 (λx.2*x) == (λx.x+x)?

algorithm - 在 Haskell 中缩短 Knuth 的算法 M(混合基数)