【发布时间】:2012-08-03 00:27:45
【问题描述】:
可能所有有经验的 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 ((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)) ...)
而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* 更基础。
【讨论】:
我不确定我会打电话给letbroken。例如,您可能出于某种原因想要在封闭环境中隐藏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 经验可言)。
【讨论】:
我被告知原因主要是历史原因,但它可能仍然成立:由于 let 进行并行分配,因此变量之间没有数据依赖关系可能会给编译器带来灵活性,以提高速度或空间并编译成更快的东西比let*。我不知道 elisp 编译器使用了多少。
在谷歌上搜索了一下,发现 Common Lisp 有一个类似的问题:LET versus LET* in Common Lisp
【讨论】:
(let ((var1 init-form-1) (var2 init-form-2) ...) ...) 的形式首先评估表达式init-form-1、init-form-1 等等,按此顺序,保存结果值。"
(let ((y (setq x 1)) (z (setq x 2))) x)。 let 并没有完全摆脱数据依赖。
let 生成哪种更好的代码,而它无法 为let* 生成。也许是一个不同的 SO 问题?