可能所有有经验的 elispers 都曾在某个时候发现,像这样的代码被破坏了:
(let ((a 3)
(b 4)
(c (+ a b)))
c)
let*
当在绑定子句中引用刚刚绑定的变量时,应该使用这种形式。
我只是想知道 - 为什么一个看似错误的行为是默认行为?let*
不管一个人将如何使用它,总是选择它是否有任何风险?
任何形式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
并行分配,变量之间没有数据依赖关系可能会给编译器带来灵活性,编译速度或空间并编译成比let*
. 我不知道 elisp 编译器使用了多少。
一些谷歌搜索揭示了 Common Lisp 的类似问题:LET 与 LET* in Common Lisp
这样做是不正确的
(let ((a 10)
(b a)) <- error
b)
这样做是正确的:
(let* ((a 10)
(b a))
b) <- will evaluate to 10.
第一种情况的错误是由于 let 的语义。
let*
将新符号添加到现有环境中,并在其中评估它们。
let
创建一个新环境,评估当前环境中的新符号,并且新环境将在所有新符号的评估结束时负责评估 (let () code ) 的代码。
let*
是语法糖
(let ((a xxx))
(let ((b a)) ...)
whilelet
是语法糖
((lambda (a)
...)
val_a)
第二种形式更常见,所以也许他们想给它一个更短的名字......
我不确定我会打电话给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 经验可以说)。