2012-08-06 92 views
4

由於可能都經歷elispers已經在某些時候發現,這樣的代碼被打破:綁定條款內指的是剛剛綁定變量時爲什麼不讓*默認讓?

(let ((a 3) 
     (b 4) 
     (c (+ a b))) 
    c) 

應該使用let*形式來代替。

我只是想知道 - 爲什麼看似錯誤的行爲默認?不管人們如何使用它,在選擇總是let*時是否有風險?

回答

6

我被告知,其原因主要是歷史的,但它可能仍然存在:由於let做並行分配,變量之間沒有數據依賴關係可能會使編譯器靈活地編譯速度或空間,編譯速度遠遠快於let*。我不知道elisp編譯器使用了多少。

一些Google搜索揭示了Common Lisp的一個類似問題:LET versus LET* in Common Lisp

+0

+1並行賦值。現在在多核環境中更重要,儘管我不確定許多編譯器會利用這一點。 – 2012-08-07 21:21:06

+2

我不確定Elisp和CL編譯器現在是如何實際執行的,但Hyperspec是CL的事實標準參考,[用不同的方式說](http://www.lispworks.com/documentation/lw50/CLHS /Body/s_let_l.htm):「形式'(let((var1 init-form-1)(var2 init-form-2)...)...)'首先評估表達式init-form-1 ','init-form-1',依此類推,**按**順序排列,保存結果值。「 – dkim 2012-08-08 22:03:07

+1

是的,它非常微妙。分配是並行完成的,但表達式的計算是連續的。看到這個特定的答案http://stackoverflow.com/questions/554949/let-versus-let-in-common-lisp/562975#562975。我想知道這是否使得基於表現的論點真的是一個神話,或者從這種放鬆還有一些優勢。 – 2012-08-09 07:51:06

4

是不正確的這樣做

(let ((a 10) 
     (b a)) <- error 
    b) 

它是正確的這樣做:

(let* ((a 10) 
     (b a)) 
    b) <- will evaluate to 10. 

在第一種情況下該錯誤是由於放手的語義。

let*爲現有環境添加了新符號,並對其進行評估。

let創建了一個新的環境,評估當前環境中的新符號,新環境在所有新符號評估結束時將會出現,評估(let()code)代碼。

let*是語法糖

(let ((a xxx)) 
    (let ((b a)) ...) 

letsyntactic sugar

((lambda (a) 
    ...) 
    val_a) 

第二種形式是更常見,所以也許他們認爲,給它一個較短的名稱..

21

任何let*表單可以是容易(和機械地)使用let表示。舉例來說,如果我們沒有足夠的let*特殊形式,我們可以表達

(let* ((a 3) 
     (b 4) 
     (c (+ a b))) 
    c) 

爲:

(let ((a 3)) 
    (let ((b 4)) 
    (let ((c (+ a b))) 
     c))) 

在另一方面,一些let形式是相當困難的,如果可能的話,用let*表示。以下let表單不能使用let*表示,而不會引入額外的臨時變量或深奧的按位操作。

(let ((x y) (y x)) ; swap x and y 
    ...) 

所以,在這個意義上,我認爲letlet*更爲根本。

2

我不確定我會打電話let損壞。例如,無論出於什麼原因,您都可能想要在封閉環境中隱藏ab,同時針對包含表達式定義c

(let ((a 11) 
     (b 12)) 
    (let ((a 3) 
     (b 4) 
     (c (+ a b))) 
    ;; do stuff here will shadowed `a' and `b' 
    c)) 
> 23 

至於風險,我不知道是否有任何本身。但是,當你不需要時,爲什麼創建多個嵌套環境?這樣做可能會有性能損失(我沒有足夠的經驗與elisp說)。