正如所有经验丰富的elispers在某些时候发现的那样,代码就像是坏了:
(let ((a 3)
(b 4)
(c (+ a b)))
c)
在引用绑定子句中的刚绑定变量时,应该使用let*
形式。
我只是想知道 - 为什么看似错误的行为是默认的?总是let*
选择是否存在风险,无论人们如何使用它?
答案 0 :(得分: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
...)
因此,从这个意义上讲,我认为let
比let*
更为基础。
答案 1 :(得分:6)
我被告知原因主要是历史性的,但它可能仍然存在:由于let
执行并行分配,变量之间没有数据依赖性可能会使编译器灵活地编译速度或空间并编译成某种东西比let*
快得多。我不知道elisp编译器使用了多少。
一些谷歌搜索揭示了Common Lisp的类似问题:LET versus LET* in Common Lisp
答案 2 :(得分:4)
这样做是不正确的
(let ((a 10)
(b a)) <- error
b)
这样做是正确的:
(let* ((a 10)
(b a))
b) <- will evaluate to 10.
第一种情况的错误是由于let的语义。
let*
向现有环境添加新符号,并在其中对其进行评估。
let
创建一个新环境,评估当前envinronemnt中的新符号,并且在评估所有新符号结束时新环境将是不可取的,以评估(let()代码的代码)。
let*
是
(let ((a xxx))
(let ((b a)) ...)
let
为syntactic sugar
((lambda (a)
...)
val_a)
第二种形式更常见,所以也许他们想给它一个更短的名字......
答案 3 :(得分:2)
我不确定我会打电话给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
至于风险,我不知道是否有任何风险。但是,为什么在不需要时创建多个嵌套环境?这样做可能会有性能损失(我没有足够的经验来说明。)