由於可能都經歷elispers已經在某些時候發現,這樣的代碼被打破:綁定條款內指的是剛剛綁定變量時爲什麼不讓*默認讓?
(let ((a 3)
(b 4)
(c (+ a b)))
c)
應該使用let*
形式來代替。
我只是想知道 - 爲什麼看似錯誤的行爲默認?不管人們如何使用它,在選擇總是let*
時是否有風險?
由於可能都經歷elispers已經在某些時候發現,這樣的代碼被打破:綁定條款內指的是剛剛綁定變量時爲什麼不讓*默認讓?
(let ((a 3)
(b 4)
(c (+ a b)))
c)
應該使用let*
形式來代替。
我只是想知道 - 爲什麼看似錯誤的行爲默認?不管人們如何使用它,在選擇總是let*
時是否有風險?
我被告知,其原因主要是歷史的,但它可能仍然存在:由於let
做並行分配,變量之間沒有數據依賴關係可能會使編譯器靈活地編譯速度或空間,編譯速度遠遠快於let*
。我不知道elisp編譯器使用了多少。
一些Google搜索揭示了Common Lisp的一個類似問題:LET versus LET* in Common Lisp
是不正確的這樣做
(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)) ...)
而let
是syntactic sugar爲
((lambda (a)
...)
val_a)
第二種形式是更常見,所以也許他們認爲,給它一個較短的名稱..
任何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
...)
所以,在這個意義上,我認爲let
比let*
更爲根本。
我不確定我會打電話let
損壞。例如,無論出於什麼原因,您都可能想要在封閉環境中隱藏a
和b
,同時針對包含表達式定義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說)。
+1並行賦值。現在在多核環境中更重要,儘管我不確定許多編譯器會利用這一點。 – 2012-08-07 21:21:06
我不確定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
是的,它非常微妙。分配是並行完成的,但表達式的計算是連續的。看到這個特定的答案http://stackoverflow.com/questions/554949/let-versus-let-in-common-lisp/562975#562975。我想知道這是否使得基於表現的論點真的是一個神話,或者從這種放鬆還有一些優勢。 – 2012-08-09 07:51:06