2012-03-28 73 views
10

我想了解我在Clojure中注意到的一些行爲。Clojure讓允許多個具有相同名稱的綁定

它可以創建一個讓具有相同綁定名稱結合重複多次:

(let [a 1 a 2 a b] a) 
; (= a 2) 

(let [a 1 a 2 a 3] a) 
; (= a 3) 

我明白,讓綁定進行評估,而這一切主要是有道理的。

我對文檔的理解是:「用let創建的本地變量不是變量,一旦創建,它們的值永遠不會改變!」

上述語法實際上是否改變了綁定的值?

這感覺就像應該引發一個錯誤。

作爲一種附帶說明的:

有趣的是,你可以輸出上面的JS與clojurescript:

var a__36584 = 1, b__36585 = 2, a__36586 = b__36585; 
var a__30671 = 1, a__30672 = 2, a__30673 = 3; 

在這裏我們可以看到,值都是實際不同的變量,它指向什麼正在發生,但一些澄清將是非常有幫助的。

回答

22

(let [a 1, a 2] a)在功能上等同於(let [a 1] (let [a 2] a)),這可能更容易理解。在後一種情況下,可以相對容易地認識到,您並未「修改」a的值,而是引入了一個名稱爲a且具有不同值的新無關變量。你可以看到(let [a 1] (let [a 2] (println a)) a)這個效果 - 它打印2,然後返回1,因爲外部的a從不改變,只是暫時隱藏。 (let [a 1, a 2] a)只是簡單地引入一個名爲a的值,該值立即超出範圍。當然,外部a是可用的,直到內部a有一個值,所以你可以做一些像(let [a 1, a (inc a)] a)

8

let clojure的行爲類似於Common Lisp的let*,也就是說,它允許更早的綁定使用。結合重新綁定,這可能是有用的,例如,當你需要以一種乾淨的方式刪除一些數據層:

(let [a some-vector, a (first a), a (:key a)] a) 

當然這不是一個錯誤。正如您已經注意到的,這些綁定內部會影響不同的變量。這實質上是clojure詞彙變量的不變性。由於這個詞彙變量rebinding具有乾淨的語義(最後一個綁定「勝利」),並且沒有理由拒絕它。

6

其他答案已經正確地指出,let語法有效地爲隱藏舊綁定的新綁定。

一個有趣的另外一點要注意的是,這可以爲Clojure的代碼優化是非常有用的,當你知道一個值將有一個特定的類型,例如:

(let [d (double d)] 
    ......) 

內讓利塊,d會然後被用作基本雙重加速,這可以顯着加速許多數學運算。

相關問題