函数式编程 - 关于 Vars Clojure 的问题



我是Clojure的新手,我读到它是一种函数式语言。它说 Clojure 没有变量,但是当我找到 (def n 5) 时,它和变量有什么区别?

我可以在之后更改 var 的值,那么它真的与变量有那么不同吗?我不明白其中的区别。

假设您所说的variable是指对可变存储位置的引用,我想主要区别(取决于您比较的语言)是,如果您动态重新绑定 Clojure 中的var是按线程进行的。

但长答案是,除非你真的需要对可变存储位置的引用,否则你通常不会在 Clojure 中使用var

Clojure倾向于不可变性和使用而不是引用进行编程。

你可以看里奇·希基(Rich Hickey)关于价值观的演讲。

总结一下,当你在Clojure中编程时,你所拥有的是,而不是对可能改变(甚至可能被另一个线程改变)的位置的引用。

所以。

(let [a 1 
      _ (println a) => prints 1
      a 2 
      _ (println a) => prints 2
     ]) 

即使你在代码中得到"改变a"的错觉,你也不会改变"旧"a你只是有一个新的值。(如果有人查看第一个定义,它仍然会看到值 1)。

实际上,您可以看到作为组合函数的赋值序列调用,其中a在作用域中被替换,但根本不是相同的"变量"。

 ((fn [a]
    (println a) => prints 1
    ((fn [a]
        (println a) => prints 2
      ) 2) 1)

尽管如此,如果您需要一个可变存储,并且可能有许多线程访问该存储,Clojure 会为您提供varsatomsrefs等。

Clojure没有变量,即可变引用,这是不正确的。但是,它们不用于在可以建模为纯数学函数的计算期间存储和查找。

不变性的概念是处理具体值而不是一个或另一个可以更改的引用。就像1是一个你不能改变的值一样,在Clojure中,向量[3 2]是你也不能改变的值。例如,如果你的算法需要将1附加到该向量,它需要创建一个新的向量,保持旧向量不变,而在命令式语言中,你可以"改变"向量,打破可能依赖它的一切。不可变性的好处是,您不必再担心这一点,并且您的代码变得不那么容易出错。

Clojure实现不可变的数据结构的方式是,这些新创建的值有效地重用了它们所基于的值的大部分内存。它们在读取和写入(即创建新版本)方面提供与可变对应项几乎相同的性能特征。你可能想在这里阅读更多关于这一点的信息,Rich Hickey在与Brian Beckmann的对话中做了一些很好的解释。

将 def 视为定义常量。可以通过再次调用 def 来更改它,但您不应该这样做。

变量的关闭是线程安全的代理。

(def counter (agent 0))
(send counter inc)
@counter
;;=> 1

您还可以访问 Java 类中的变量。

新类

(def object (ClassName.))

硒值

(.fieldName object)

设置值

(set! (.fieldName object) 5)

没有变量的"全部"要点是使程序自动线程安全。这是因为线程错误将"总是"在该线程 1 上失败,告诉变量 a 是 1,线程 b 告诉 a 是 2,然后某些事情失败。这也是使用纯函数的原因 - 没有变量"no"线程问题。

另请参阅这个问题:Ref,Var,Agent,Atom之间的Clojure差异,示例和这个Clojure:vars,atoms和refs(哦,天哪)。

将"视为 80% 或更多 - 而不是 100%。

在大多数编程语言中,变量的概念是可以包含"值"的"名称"和"位置"。因此,例如,在 Python 中,x = 4创建一个名称"x"和一个与包含值"4"的"x"关联或连接的位置。如果我们后来说x = 5,"x"是相同的名称,连接到相同的位置,但值现在是"5"。

Clojure使程序员能够控制名称,位置以及如何按菜单更改位置。为了获得相同的行为,人们会在Clojure (def x (atom 4))中说。def创建名称"x",位置由atom创建,原子中的值由文字4创建。要更改 Clojure 中的值,需要评估 (reset! x 5) .这说找到名称为"x"的原子/位置,并将值"5"放入其中。

最新更新