4

可能所有有经验的 elispers 都曾在某个时候发现,像这样的代码被破坏了:

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

let*当在绑定子句中引用刚刚绑定的变量时,应该使用这种形式。

我只是想知道 - 为什么一个看似错误的行为是默认行为?let*不管一个人将如何使用它,总是选择它是否有任何风险?

4

4 回答 4

22

任何形式let*都可以使用. 例如,如果我们没有特殊的形式,我们可以表达letlet*

(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*.

于 2012-08-06T01:28:51.320 回答
6

我被告知原因主要是历史原因,但它可能仍然成立:由于let并行分配,变量之间没有数据依赖关系可能会给编译器带来灵活性,编译速度或空间并编译成比let*. 我不知道 elisp 编译器使用了多少。

一些谷歌搜索揭示了 Common Lisp 的类似问题:LET 与 LET* in Common Lisp

于 2012-08-07T08:57:55.310 回答
4

这样做是不正确的

(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)

第二种形式更常见,所以也许他们想给它一个更短的名字......

于 2012-08-06T01:14:07.977 回答
2

我不确定我会打电话给let坏了。例如,您可能出于某种原因想要在封闭环境中进行阴影处理,同时a针对封闭表达式进行定义。bc

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

至于风险,我不知道本身是否有风险。但是为什么在你不需要的时候创建多个嵌套环境呢?这样做可能会降低性能(我没有足够的 elisp 经验可以说)。

于 2012-08-06T01:31:05.330 回答