问题描述
为了
(let ((fact
(lambda (n)
(if (= n 1)
1
(* n (fact (- n 1)))))))
(fact 10))
方案出错,我想确认我的推理是否正确,
因为 (let ((var1 arg1)) body)
相当于在 var1
绑定到 arg1
的环境中评估正文。
上面,当我们在绑定调用之前尝试计算 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)
解决方法
原因是“变量事实未绑定”错误仅在我们实际尝试查找“事实”的值时发生。考虑
(let ((fact (lambda (n) (if (= n 0)
1
(* n (fact (- n 1)))))))
(fact 0))
这不会导致错误,因为我们没有输入 else
子句,因此不会尝试使用未绑定变量 fact
的值。但是,对 1
尝试相同的操作确实会给我们带来错误。
我们在使用 define
时不会遇到错误,因为 define
和 let
有不同的规则。在 let
绑定中,您创建了一个新的词法作用域,fact
绑定中的 let
绑定不会从 let
“泄漏”到外部世界.由于 let
赋值的右侧只能引用来自外部世界的绑定,因此右侧不能引用绑定 fact
。
然而,在 define
语句中,重点是在当前作用域中引入一个新的绑定(而不是创建一个新的作用域)。在 define
绑定中,赋值的右侧必须仅引用来自外部世界的变量 - 但这可能包括 define
d 的变量,只要访问它隐藏在 {{ 1}} 并因此懒惰地完成。
事实上,考虑以下几点:
lambda
在这种情况下,当我们实际调用 (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-odd
处于相同的范围内define
,因此我们可以查找它的值。类似地,当我们调用 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-odd
不能与 is-even
语句中创建的 is-odd
相同,因为此 let
是本地绑定,因此不能在定义 is-odd
的更广范围内引用。
如果我们使用“动态范围”的老式 LISP 概念,也称为“按名称调用语义”,则后一个示例将工作得很好。但是,Scheme 不支持动态范围(在我看来这绝对是最好的)。