SICP 递归 let 定义

SICP recursive let definition

对于

(let ((fact 
        (lambda (n)
            (if (= n 1)
                1
                (* n (fact (- n 1)))))))
(fact 10))

方案出错,我想确认我的推理是否正确,

因为(let ((var1 arg1)) body)相当于在var1绑定到arg1的环境中评估body。

上面,当我们试图在绑定调用之前计算 arg1 时,我们找不到引用,因为这正是我们试图绑定的引用

但是为什么不抛出

(let ((fact (lambda (n) 
             (if (= n 1) 1 (* n (fact (- n 1))))))) 
  (display 10))

因为我们也试图在那里进行绑定。

还有为什么这个不抛出呢

(define factorial
    (lambda (n)
      (if (= n 0)
          1
          (* n (factorial (- n 1))))))
          
(factorial 10)

原因是只有当我们实际尝试查找“fact”的值时才会出现“variable fact is not bound”错误。考虑

(let ((fact (lambda (n) (if (= n 0)
                            1
                            (* n (fact (- n 1)))))))
 (fact 0))

这不会导致错误,因为我们没有输入 else 子句,因此不会尝试使用未绑定变量 fact 的值。然而,用 1 尝试同样的事情确实给了我们错误。

我们在使用define时不会运行出错,因为definelet有不同的规则。在 let 绑定中,您创建了一个新的词法范围,并且 let 绑定中 fact 的绑定不会从 let 中“泄漏”到外界.由于 let 赋值的右侧只能引用来自外部世界的绑定,因此右侧不能引用绑定 fact.

但是,在 define 语句中,重点是在当前作用域中引入新绑定(而不是创建新作用域)。在 define 绑定中,赋值的右侧必须仅引用来自外部世界的变量——但这可能包括变量 defined,只要访问它隐藏在 [= 后面28=] 因此懒惰地完成了。

事实上,考虑以下几点:

(define is-even
  (lambda (n) (or (= n 0)
                  (is-odd (- n 1)))))

(define is-odd
  (lambda (n) (and (not (= n 0))
                   (is-even (- n 1)))))

(display (is-odd 55))

在这种情况下,当我们实际调用 is-even 时,is-odddefined 在与 is-even 相同的范围内,因此我们可以查找它价值。类似地,当我们调用 is-odd 时,is-even 已经定义,因此我们对其值有一个绑定。

请注意以下工作:

(define is-even
  (lambda (n) (or (= n 0)
                  (is-odd (- n 1)))))

(display (let ((is-odd (lambda (n) (and (not (= n 0))
                                        (is-even (- n 1))))))
            (is-odd 55)))

这是因为这里的 is-odd 绑定 而不是 is-even 在同一范围内。它是在比 is-even 更本地化的绑定中定义的。 is-even 定义中引用的 is-odd 不能与 let 语句中创建的 is-odd 相同,因为此 is-odd 是本地绑定,因此不能在 is-even 定义的更广泛范围内引用。

如果我们使用“动态范围”的老式 LISP 概念,也称为“按名称调用语义”,后一个示例将工作得很好。但是,Scheme 不支持动态范围(在我看来这绝对是最好的)。