【问题标题】:Why isn't let* the default let?为什么不让*默认让?
【发布时间】:2012-08-03 00:27:45
【问题描述】:

可能所有有经验的 elispers 都曾发现,类似代码已损坏:

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

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

我只是想知道 - 为什么一个看似错误的行为是默认行为?不管怎么用,总是选择let*会有风险吗?

【问题讨论】:

    标签: emacs lisp elisp


    【解决方案1】:

    这样做是不正确的

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

    letsyntactic sugar

    ((lambda (a)
      ...)
      val_a)
    

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

    【讨论】:

      【解决方案2】:

      任何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
        ...)
      

      所以,从这个意义上说,我认为letlet* 更基础。

      【讨论】:

        【解决方案3】:

        我不确定我会打电话给letbroken。例如,您可能出于某种原因想要在封闭环境中隐藏ab,同时针对封闭表达式定义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 经验可言)。

        【讨论】:

          【解决方案4】:

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

          在谷歌上搜索了一下,发现 Common Lisp 有一个类似的问题:LET versus LET* in Common Lisp

          【讨论】:

          • +1 用于并行分配。现在在多核环境中更为重要,尽管我不确定许多编译器会利用这一点。
          • 我不确定 Elisp 和 CL 编译器现在的实际情况如何,但是 Hyperspec,一个事实上的 CL 标准参考,says differently:“(let ((var1 init-form-1) (var2 init-form-2) ...) ...) 的形式首先评估表达式init-form-1init-form-1 等等,按此顺序,保存结果值。"
          • 是的,它非常微妙。赋值是并行完成的,但表达式的计算是顺序的。请参阅此特定答案stackoverflow.com/questions/554949/…。我想知道这是否使基于性能的论点真的成为一个神话,或者这种放松仍然有一些优势。
          • 这是一个顺序很重要的例子:(let ((y (setq x 1)) (z (setq x 2))) x)let 并没有完全摆脱数据依赖。
          • 当然,我完全同意!这就是为什么我说原因主要是历史原因(在 CL 标准的早期草案或不同的 lisp 中可能有所不同)。我问的是,编译器仍然可以为let 生成哪种更好的代码,而它无法let* 生成。也许是一个不同的 SO 问题?
          猜你喜欢
          • 2023-01-30
          • 1970-01-01
          • 1970-01-01
          • 2013-11-29
          • 2022-01-18
          • 2010-12-17
          • 2015-10-08
          • 1970-01-01
          • 2021-02-09
          相关资源
          最近更新 更多