在动态范围假设下,下面的代码会返回错误。
(let ((f (lambda (g)
(lambda (n)
(if (zero? n)
1
(* n ((g g) (- n 1))))))))
((f f) 5))
我的答案是0,因为:
n*(n-1)*(n-2)*(n-3)*(n-3)*(n-4)*1;; since the call with n=0, n bound to 0
0*0*0*0*1
我在这里缺少什么?
答案 0 :(得分:2)
(define test
(let ((x 10))
(lambda () x)))
这里我们从x
是局部变量的范围返回lambda函数。在词法范围下,环境将附加到创建的lambda函数。这个环境由自由变量之上的绑定变量组成,这些变量在创建lambda函数时可用 - 这里,x
,绑定到10.因此,当调用返回的lambda函数时,它的{{1 }}只能是x
。
在动态范围内,10
是死代码。创建的lambda函数不存储其词法环境,因此当它被调用时,let
将在实际的时间中被新鲜查找电话。到那时,名为x
且值为x
的变量将不再存在。 lambda查找的10
将是您在呼叫时x
绑定的任何内容:
x
当然:
(let ((x 20))
(test))
; ==> 20
所以对你的代码来说也是同样的问题。无论(test); == ERROR: Unbound variable x
在评估g
时是什么,创建lambda函数,当返回lambda函数时超出范围,因此当调用返回的lambda函数时,将查看(lambda (n) ...)
新鲜,并且将像呼叫时一样g
将被绑定,就像之前一样。为了使其在动态范围内工作,您可以这样做:
g
这里的区别在于(let ((f (lambda (g n)
(if (zero? n)
1
(* n (g g (- n 1)))))))
(f f 5))
永远不会超出范围。这适用于动态和词法范围。您可以将其简化为动态范围,如下所示:
g
在词汇范围内,(let ((f (lambda (n) ; ((lambda (f) (f 5))
(if (zero? n) ; (lambda (n)
1 ; (if (zero? n)
(* n (f (- n 1))))))) ; 1
(f 5)) ; (* n (f (- n 1))))))
内的f
是未绑定的变量,但在动态范围(lambda (n) ...)
首先建立,并且在调用f
之后,它仍可供所有人使用嵌套调用(f 5)
,(f 4)
等,直到(f 3)
内(f 5)
的评估完成为止;只有当(lambda (f) (f 5))
退出f
并返回lambda
的结果时,(f 5)
才会被解除,销毁。
在Paul Grahams nice wrap-up of McCarthys original lisp paper中,他提到文件中有一个错误。第一个高阶函数maplist
具有x
作为list参数的名称。在演示diff
中,他将函数传递给maplist
作为参数x
。这两个在第一对之后发生碰撞,因此由于动态范围而无法工作。动态范围非常容易出错,并且在所有全局变量都是动态的Common Lisp中,*earmuffs*
命名约定是必须避免无数小时查找全局变更函数以执行与预期完全不同的操作。
答案 1 :(得分:0)
使用动态范围,g
将是未定义的,因为第6行没有名为g
的变量。