你能解释一下这个LISP函数吗?为什么动态范围和词汇范围会出现问题?
在阅读一些lisp历史记录时,我遇到了以下函数:你能解释一下这个LISP函数吗?为什么动态范围和词汇范围会出现问题?,lisp,scoping,lexical-scope,dynamic-scope,Lisp,Scoping,Lexical Scope,Dynamic Scope,在阅读一些lisp历史记录时,我遇到了以下函数: (define (testr x p f u) (if (p x) (f x) (if (atom? x) (u) (testr (cdr x) p f (lambda () (testr (car x) p f u)))))) 根据麦卡锡
(define (testr x p f u)
(if (p x)
(f x)
(if (atom? x)
(u)
(testr (cdr x)
p
f
(lambda ()
(testr (car x) p f u))))))
根据麦卡锡的说法,“困难在于,当内部递归发生时,car[x]想要的值是外部值,但实际使用的是内部值。在现代术语中,需要词汇范围界定,并获得动态范围界定。”
我不太清楚他所指的“外部价值”和“内部价值”是什么,也看不出这个函数在使用动态作用域进行评估时是如何表现的。我能理解lambda对“x”的阴影,但它是一个零参数函数
(实际上很难找到这个函数,因为它似乎从网页本身中丢失了。只有在浏览了此处的images.tex文件后:我才找到它)。让我们用Lisp,这里是Common Lisp。在通用Lisp中,可以很容易地在动态绑定和词汇绑定之间切换
(defun testr (x p f u)
(if (funcall p x)
(funcall f x)
(if (atom x)
(funcall u)
(testr (cdr x)
p
f
(lambda ()
(testr (car x) p f u))))))
词汇范围
本例使用词汇绑定
(defun testr (x p f u)
(if (funcall p x)
(funcall f x)
(if (atom x)
(funcall u)
(testr (cdr x)
p
f
(lambda ()
(testr (car x) p f u))))))
函数应该做什么?它应该在嵌套列表中找到P
为true的最右边的元素
如您所见,返回的值与预期的一样
动态范围
如果使用动态绑定,则会发生以下情况:
(defun testr (x p f u)
(declare (special x p f u)) ; use dynamic binding
(if (funcall p x)
(funcall f x)
(if (atom x)
(funcall u)
(testr (cdr x)
p
f
(lambda ()
(testr (car x) p f u))))))
CL-USER 38 > (testr '(1 (2 3) 3 (6 6 6))
(lambda (y) (and (numberp y) (oddp y)))
#'identity
nil)
Stack overflow (stack size 15998).
如果我们定义了ecar
类似于car
,但在项目不是cons
时发出错误信号:
(defun ecar (item)
(if (consp item)
(car item)
(error "Item ~a not a cons" item)))
(defun testr (x p f u)
(declare (special x p f u))
(if (funcall p x)
(funcall f x)
(if (atom x)
(funcall u)
(testr (cdr x)
p
f
(lambda ()
(testr (ecar x) p f u))))))
CL-USER 52 > (testr '(1 2)
(lambda (y)
(and (numberp y) (oddp y)))
#'identity
nil)
Error: Item NIL not a cons
在列表的末尾,x
是nil
,这不是一个缺点,因此(ecar x)
发出错误信号
问题
(defun testr (x p f u)
(declare (special x p f u)) ; use dynamic binding
(if (funcall p x)
(funcall f x)
(if (atom x)
(funcall u) ; INNER: here the lambda function is called
; with dynamic binding, the value of X
; is the current binding of X from
; the current call.
: at the end of a list, X would be NIL.
; Inside the lambda function then X would be NIL, too.
; (car x) -> returns NIL
; then we are in an endless recursion
; OUTER: with lexical binding, the value
; of X would be the value of some
; binding where the function was
; defined and called earlier.
(testr (cdr x)
p
f
(lambda () ; our lambda function
(testr (car x) ; the reference to X
p f u))))))
简单跟踪
让我们看看它是如何访问元素的:
词汇:
CL-USER 42 > (testr '(1 (2 3) 4 (6 8 10))
(lambda (y)
(print (list :test y))
(and (numberp y) (oddp y)))
#'identity
nil)
(:TEST (1 (2 3) 4 (6 8 10)))
(:TEST ((2 3) 4 (6 8 10)))
(:TEST (4 (6 8 10)))
(:TEST ((6 8 10)))
(:TEST NIL) ; it has reached the end of the top list
(:TEST (6 8 10)) ; it recurses down the rightmost sublist
(:TEST (8 10))
(:TEST (10))
(:TEST NIL) ; end of the rightmost sublist
(:TEST 10) ; checks the elements of the rightmost sublist
(:TEST 8)
(:TEST 6)
(:TEST 4) ; back up, next element of the top list
(:TEST (2 3)) ; next sublist of the top list
(:TEST (3))
(:TEST NIL) ; end of that sublist
(:TEST 3) ; checks right element, found
3
动态:
CL-USER 40 > (testr '(1 (2 3) 4 (6 8 10))
(lambda (y)
(print (list :test y))
(and (numberp y) (oddp y)))
#'identity
nil)
(:TEST (1 (2 3) 4 (6 8 10)))
(:TEST ((2 3) 4 (6 8 10)))
(:TEST (4 (6 8 10)))
(:TEST ((6 8 10)))
(:TEST NIL) ; it reaches the end of the top list
(:TEST NIL) ; it goes into the endless recursion
(:TEST NIL)
(:TEST NIL)
(:TEST NIL)
(:TEST NIL)
...