在clojure中的不变性是否与按值传递不同?

时间:2013-12-10 17:21:18

标签: clojure functional-programming

我刚刚开始使用Clojure而且我没有fp经验,但我注意到的第一件事就是强调不变性。然而,我对重点感到有些困惑。看起来您可以轻松地重新定义全局变量,从根本上为您提供一种改变状态的方法。我能看到的最重要的区别是函数参数是按值传递的,不能在函数内重新定义。这是一个显示我的意思的repl片段:

 towers.core=> (def a "The initial string")
 #'towers.core/a
 towers.core=> a
 "The initial string"
 towers.core=> (defn mod_a [aStr]
     #_=>   (prn aStr)
     #_=>   (prn a)
     #_=>   (def aStr "A different string")
     #_=>   (def a "A More Different string")
     #_=>   (prn aStr)
     #_=>   (prn a))
 #'towers.core/mod_a
 towers.core=> a
 "The initial string"
 towers.core=> (mod_a a)
 "The initial string"
 "The initial string"
 "The initial string"
 "A More Different string"
 nil
 towers.core=> a
 "A More Different string"

如果我通过将其视为价值传递而开始理解clojure中的不变性,那么我缺少什么?

5 个答案:

答案 0 :(得分:9)

按值调用和不变性是两个完全不同的概念。实际上,变量不变性的一个优点是这些变量可以通过名称或引用传递,而不会对程序行为产生任何影响。

简而言之:不要将它们视为联系。

答案 1 :(得分:4)

在一个clojure脚本/类中通常很少是“def”d,它主要用于生成在类之外使用的值。而是在您的方法中根据需要使用let绑定创建值。

def用于定义变量,如Clojure Programming中所述:

  

顶级函数和值都存储在变量中,即   使用def特殊形式或当前命名空间在当前命名空间中定义   它的衍生物。

你在函数中使用def不是创建局部变量,而是创建一个新的全局变量,并且每次都用新的变量有效地替换旧的引用。

当你转向使用let时,你会看到不变性是如何工作的,例如使用seqs之类的东西,这些东西可以被使用而不会被别人读过它们(例如在java中的列表上的迭代) ),例如

(let [myseq (seq [1 2 3 4 5])
          f (first myseq)
          s (second myseq) 
        sum (reduce + myseq)]
  (println f s sum))
;; 1 2 15

正如您所看到的,(first myseq)从序列中“取走”了一个项目并不重要。因为序列myseq是不可变的,它仍然是相同的,并且不受其上的操作的影响。另外,请注意上面的代码中没有一个def,赋值发生在let绑定中,其中创建了myseq,f,s和sum的值(并且在sexp的其余部分中是不可变的)。

答案 2 :(得分:3)

是的,不变性与传值不同,你错过了一些关于你的例子中发生的事情的重要细节:

  1. 价值突变变量重新绑定。您的代码举例说明了重新绑定,但实际上并没有改变值。

  2. <强>阴影即可。您的本地aStr会影响您的全局aStr,因此您无法看到全局的(def a ...) - 尽管它仍然存在 - 因此(def aStr ...)和{{1}的效果之间没有区别}} 这里。您可以在运行函数后验证是否已创建全局。

  3. 最后一点:Clojure并没有强迫你纯粹的功能 - 它有逃生舱,你可以负责任地使用它们。重新绑定变量就是其中一个逃逸舱口。

答案 3 :(得分:2)

只是注意技术上Java,并且扩展Clojure(在JVM上)是strictly pass by value。在许多情况下,传递的东西是对其他人可能正在阅读的结构的引用,但是因为它是不可变的,所以没有人会从你下面改变。重要的一点是,在你将参考文献传递给某个东西后,可变性和不变性就会发生,而Marcin指出它们确实是截然不同的。

答案 4 :(得分:0)

我认为Clojure中的大部分不变性都存在于(大部分)内置数据结构类型和(大多数)允许操作的函数中......呃,不,修改......不,真的,从中构建新的数据结构。有类似数组的东西,但是你不能修改它们,有列表,但是你不能修改它们,有哈希映射,但是你不能修改它们等等,以及使用它们的标准工具实际上创建新的数据结构,即使它们看起来像是新手,就好像他们正在执行就地修改一样。所有这些确实会产生很大的不同。