2011-06-03 100 views
16

在Clojure中,變量範圍+ EVAL Clojure中

(def x 3) 
(eval '(prn x)) 

打印3,而

(let [y 3] 
    (eval '(prn y))) 

(binding [z 3] (eval '(prn z))) 

產生 '無法解析變種' 異常。

根據http://clojure.org/evaluation,eval,load-string等生成臨時名稱空間來評估其內容。因此,我預計上述代碼示例都不能工作,因爲(def x 3)是在我當前的命名空間中完成的,而不是由eval創建的。

  1. 爲什麼第一個代碼示例工作,而不是最後兩個?
  2. 我怎樣才能eval綁定變量不使用def

謝謝!

回答

14

1:

這不工作的原因是(或多或少)你的鏈接頁面上給出:

It is an error if there is no global var named by the symbol […] 

和:

[...]

  1. 在當前命名空間中執行查找以查看是否存在映射 從符號到var。如果是這樣,則 值是該符號引用的var的綁定值。

  2. 這是一個錯誤。

eval評估在詞法環境空(在CL-行話null)形式。這意味着,你不能從調用者的作用域訪問詞法變量綁定。此外,binding爲現有變量創建了新的綁定,這就是爲什麼您不能「自行」使用它,而沒有declare d或def您嘗試綁定的變量。此外,詞法變量(至少在CL中,但如果Clojure不是這種情況,我會感到驚訝)在運行時已經不復存在 - 它們被轉換爲地址或值。

另請參閱我的older post關於此主題。

2 .:

所以,你必須使用動態變量。你可以避開明確def,但你仍然至少需要declare它們(這def小號變種名稱,而不綁定):

user=> (declare x) 
#'user/x 
user=> (binding [x 10] (eval '(prn x))) 
10 
nil 

順便說一句:我想你知道爲什麼你需要的eval,並且其使用當其他解決方案適合時,爲considered evil

+3

請注意,這在Clojure 1.3中不起作用。你必須使用'(declare ^:dynamic x)'。 – danlei 2011-06-03 00:44:33

+0

謝謝!我現在明白我的問題了 - 我假設「null詞法作用域」在「空名稱空間」中也有意義,但快速測試顯示「eval」在當前名稱空間內工作,這就是爲什麼它可以訪問名稱空間變量但不能詞彙變量。 非常好的答案,你的鏈接也很有幫助! – gilesc 2011-06-03 17:10:14

+0

不客氣!我很高興它有幫助。另外,感謝接受。 – danlei 2011-06-03 17:53:34