如果可以使用def重新定义变量,那么如何将其视为不可变的?


10

尝试学习Clojure会让您不停地被告知,Clojure是如何处理不可变数据的。但是您可以使用defright 轻松地重新定义变量?我知道Clojure开发人员可以避免这种情况,但是您可以避免在任何语言中都一样地更改变量。有人可以向我解释这有什么不同,因为我认为我正在阅读的教程和书籍中缺少此内容。

举个例子

a = 1
a = 2

在Ruby(或blub,如果您愿意)中与

(def a 1)
(def a 2)

在Clojure中?

Answers:


9

正如您已经注意到的那样,在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之类的其他语言一样,可以避免这种情况,但是defClojure中的构造旨在访问和改变全局环境。实际上,这与赋值可以在命令性语言(如Java,C ++,Python)中执行的操作非常相似。

尽管如此,Clojure提供了许多避免突变的结构和库,您完全可以不用它就可以走很长一段路。到目前为止,避免突变是Clojure中首选的编程样式。


1
到目前为止,避免突变是首选的编程样式。我建议这句话现在适用于每种语言。不只是Clojure;)
David Arno

2

Clojure就是关于不变数据的

Clojure是关于通过控制突变点(即s,s,s和s)来管理可变状态。当然,尽管您通过互操作使用的任何Java代码都可以随心所欲。RefAtomAgentVar

但是您可以使用def轻松重新定义变量吗?

如果您的意思是将Var(而不是例如局部变量)绑定到其他值,则可以。实际上,如Vars和Global Environment中所述Vars被专门包含为Clojure的四种“引用类型”之一(尽管我会说它们主要是指dynamic Var)。

使用Lisps,通过REPL执行交互式,探索性编程活动已有很长的历史。这通常涉及定义新的变量和函数,以及重新定义旧的变量和函数。但是,在REPL之外,将defa Var视为较差的形式。


1

摘自Clojure,勇敢而真实

例如,在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!"

一个人不能轻易在Ruby中做同样的事情吗?给出的建议只是定义一个返回值的函数。Ruby也有功能!
埃文·扎米尔

是的我知道。但是Clojure并没有鼓励采用一种强制性的方法来解决所提出的问题(例如更改绑定),而是采用了功能范式。
Tiago Dall'Oca's
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.