是否可以在不做任何更改的情况下从这个 SICP 第 3 章代码中简单地删除 let 块?

Can the let block be trivially removed from this SICP chapter 3 code without anything changing?

Chapter 3 of SICP 包含这个不寻常的代码块。

(define (propagate)
  (if (empty-agenda? the-agenda)
      'done
      (let ((first-item (first-agenda-item the-agenda)))
        (first-item)
        (remove-first-agenda-item! the-agenda)
        (propagate))))

在我看来,first-item 在这里只用了一次,所以我认为 let 没有必要。是否有任何特殊原因导致以下代码不等价?

(define (propagate)
  (if (empty-agenda? the-agenda)
      'done
      (begin
        (first-agenda-item the-agenda)
        (remove-first-agenda-item! the-agenda)
        (propagate))))

你的转型想法非常好,但你做错了,正如肖恩在评论中所说。

最简单的错误示例是将代码转换成这样:

(let ((f something))
  (f)
  x)

进入

(begin
  something
  x)

请注意,此处遗漏了对 (f) 的额外调用:原始版本计算一个函数 然后调用它 ,而转换计算一个函数但抛出它走了。更准确的转换是:

(begin
  (something)
  x)

"The idea of a let block that is not doing variable assignment is strange to me."

但它会进行变量初始化和变量引用。可以写成

(define (propagate)
  (if (empty-agenda? the-agenda)
      'done
      (let ((first-item #f))
        (set! first-item (first-agenda-item the-agenda))
        (first-item)
        (remove-first-agenda-item! the-agenda)
        (propagate))))

你写的相当于

(define (propagate)
  (if (empty-agenda? the-agenda)
      'done
      (let ((first-item #f))
        (set! first-item (first-agenda-item the-agenda))
         first-item                              ; NB! _No_ _Parens_
        (remove-first-agenda-item! the-agenda)
        (propagate))))

您的行 (first-agenda-item the-agenda) returns the-agenda 中的第一个值但未使用它 -- 不对其执行任何操作.

但原始的 (first-item) 通过名称引用该值,first-item,并且 使用 它即调用该值作为不带参数的函数。

遵循以等代替等的原则,在代码中(即只处理,而不处理任何places in computer memory),我们在代码的纯子集中替换name

                            ; impure: deals with a named place in computer memory
        (set! first-item (first-agenda-item the-agenda) )
                            ; pure: refers to a value by the name it was given
        (     first-item                                )

及其价值

        (                (first-agenda-item the-agenda) )