尝试学习Clojure会让您不停地被告知,Clojure是如何处理不可变数据的。但是您可以使用def
right 轻松地重新定义变量?我知道Clojure开发人员可以避免这种情况,但是您可以避免在任何语言中都一样地更改变量。有人可以向我解释这有什么不同,因为我认为我正在阅读的教程和书籍中缺少此内容。
举个例子
a = 1
a = 2
在Ruby(或blub,如果您愿意)中与
(def a 1)
(def a 2)
在Clojure中?
尝试学习Clojure会让您不停地被告知,Clojure是如何处理不可变数据的。但是您可以使用def
right 轻松地重新定义变量?我知道Clojure开发人员可以避免这种情况,但是您可以避免在任何语言中都一样地更改变量。有人可以向我解释这有什么不同,因为我认为我正在阅读的教程和书籍中缺少此内容。
举个例子
a = 1
a = 2
在Ruby(或blub,如果您愿意)中与
(def a 1)
(def a 2)
在Clojure中?
Answers:
正如您已经注意到的那样,在Clojure中不鼓励使用可变性这一事实并不意味着它是禁止的,并且没有支持它的结构。因此,def
您可以使用与其他语言中的赋值类似的方式来更改/更改环境中的绑定,这是正确的(请参阅vars的Clojure文档)。通过在全局环境中更改绑定,您还可以更改使用这些绑定的数据对象。例如:
user=> (def x 1)
#'user/x
user=> (defn f [y] (+ x y))
#'user/f
user=> (f 1)
2
user=> (def x 100)
#'user/x
user=> (f 1)
101
请注意,在重新定义了绑定之后x
,函数f
也发生了变化,因为其主体使用了该绑定。
与语言比较这其中重新定义变量不会删除旧的结合,但只是阴影它,也就是说,它使得它在新的定义后到来范围无形。查看如果在SML REPL中编写相同的代码会发生什么:
- val x = 1;
val x = 1 : int
- fun f y = x + y;
val f = fn : int -> int
- f 1;
val it = 2 : int
- val x = 100;
val x = 100 : int
- f 1;
val it = 2 : int
请注意,在的第二个定义之后x
,该函数f
仍使用定义时作用x = 1
域内的绑定,即该绑定val x = 100
不会覆盖先前的binding val x = 1
。
底线:Clojure允许更改全局环境并在其中重新定义绑定。像SML之类的其他语言一样,可以避免这种情况,但是def
Clojure中的构造旨在访问和改变全局环境。实际上,这与赋值可以在命令性语言(如Java,C ++,Python)中执行的操作非常相似。
尽管如此,Clojure提供了许多避免突变的结构和库,您完全可以不用它就可以走很长一段路。到目前为止,避免突变是Clojure中首选的编程样式。
Clojure就是关于不变数据的
Clojure是关于通过控制突变点(即s,s,s和s)来管理可变状态。当然,尽管您通过互操作使用的任何Java代码都可以随心所欲。Ref
Atom
Agent
Var
但是您可以使用def轻松重新定义变量吗?
如果您的意思是将Var
(而不是例如局部变量)绑定到其他值,则可以。实际上,如Vars和Global Environment中所述,Var
s被专门包含为Clojure的四种“引用类型”之一(尽管我会说它们主要是指dynamic Var
)。
使用Lisps,通过REPL执行交互式,探索性编程活动已有很长的历史。这通常涉及定义新的变量和函数,以及重新定义旧的变量和函数。但是,在REPL之外,将def
a Var
视为较差的形式。
例如,在Ruby中,您可以对变量执行多次分配以建立其值:
severity = :mild error_message = "OH GOD! IT'S A DISASTER! WE'RE " if severity == :mild error_message = error_message + "MILDLY INCONVENIENCED!" else error_message = error_message + "DOOOOOOOMED!" end
您可能很想在Clojure中执行类似的操作:
(def severity :mild) (def error-message "OH GOD! IT'S A DISASTER! WE'RE ") (if (= severity :mild) (def error-message (str error-message "MILDLY INCONVENIENCED!")) (def error-message (str error-message "DOOOOOOOMED!")))
但是,更改与这样的名称相关联的值可能会使您更难理解程序的行为,因为更难知道哪个值与名称相关联或为什么该值可能已更改。Clojure有一套用于处理变更的工具,您将在第10章中了解到。学习Clojure时,您会发现几乎不需要更改名称/值关联。这是编写前面代码的一种方法:
(defn error-message [severity] (str "OH GOD! IT'S A DISASTER! WE'RE " (if (= severity :mild) "MILDLY INCONVENIENCED!" "DOOOOOOOMED!"))) (error-message :mild) ; => "OH GOD! IT'S A DISASTER! WE'RE MILDLY INCONVENIENCED!"