Common Lisp (SBCL) 中的动态变量闭包
Dynamic Variable Closure in Common Lisp (SBCL)
我了解这段代码的工作原理:
(defvar *nums* '(2 3 5))
(defun print-nums ()
(format t "~a~%" *nums*))
(print-nums)
-> (2 3 5)
-> NIL
我什至明白动态绑定变量 *nums*
的新值是如何在这段代码中传递给 print-nums
的:
(let ((*nums* '(1 2 3)))
(print-nums))
-> (1 2 3)
-> NIL
但为什么下面的代码不以同样的方式工作?
(defvar *my-nums-f* (let ((*nums* '(1 2 3)))
#'(lambda () (format t "~a~%" *nums*))))
(funcall *my-nums-f*)
-> (2 3 5)
-> NIL
闭包的概念不适用于动态绑定变量,还是我做错了什么?如果我错误地理解了闭包的概念,谁能给我解释一下?
(defvar *my-nums-f* (let ((*nums* '(1 2 3)))
#'(lambda () (format t "~a~%" *nums*))))
这里我们设置*my-nums-f*
为
的结果
(let ((*nums* '(1 2 3)))
#'(lambda () (format t "~a~%" *nums*)))
这个表单首先动态绑定*nums*
到'(1 2 3)
,然后返回#'(lambda () (format t "~a~%" *nums*))
,这是一个函数。最后,我们将 *nums*
重置为 '(2 3 5)
。
(funcall *my-nums-f*)
这里我们调用存储在*my-nums-f*
中的函数,即(lambda () (format t "~a~%" *nums*))
。此函数取 *nums*
的当前值,即 '(2 3 5)
.
-> (2 3 5)
-> NIL
您似乎期望 lambda
以某种方式内联函数主体中使用的所有变量的当前值,这确实会在该点导致 (format t "~a~%" '(1 2 3))
。
但这不是它的工作原理:函数引用变量本身,而不是变量值的快照。这就是为什么该函数在调用时会看到 *nums*
的当前值。
这是模拟动态变量的另一种外观。假设 NUMS 是在调用 BAR 之前绑定到 '(2 3 5) 的动态变量。 NUMS 在 lambda 中是免费的,并且是封闭的。但是,在 lambda returns 之前,NUMS 是反弹。这就是当动态 var 超出将新绑定推送到它的小值堆栈的词法范围时发生的情况。
(defun bar ()
(let ((nums '(1 2 3)))
(prog1
(lambda () nums)
(setf nums '(2 3 5)))))
SBCL 会告诉您它是否创建了闭包:#<FUNCTION ...>
与 #<CLOSURE ...>
。您的代码不会创建闭包,因为 lambda 主体中没有自由变量。
我了解这段代码的工作原理:
(defvar *nums* '(2 3 5))
(defun print-nums ()
(format t "~a~%" *nums*))
(print-nums)
-> (2 3 5)
-> NIL
我什至明白动态绑定变量 *nums*
的新值是如何在这段代码中传递给 print-nums
的:
(let ((*nums* '(1 2 3)))
(print-nums))
-> (1 2 3)
-> NIL
但为什么下面的代码不以同样的方式工作?
(defvar *my-nums-f* (let ((*nums* '(1 2 3)))
#'(lambda () (format t "~a~%" *nums*))))
(funcall *my-nums-f*)
-> (2 3 5)
-> NIL
闭包的概念不适用于动态绑定变量,还是我做错了什么?如果我错误地理解了闭包的概念,谁能给我解释一下?
(defvar *my-nums-f* (let ((*nums* '(1 2 3)))
#'(lambda () (format t "~a~%" *nums*))))
这里我们设置*my-nums-f*
为
(let ((*nums* '(1 2 3)))
#'(lambda () (format t "~a~%" *nums*)))
这个表单首先动态绑定*nums*
到'(1 2 3)
,然后返回#'(lambda () (format t "~a~%" *nums*))
,这是一个函数。最后,我们将 *nums*
重置为 '(2 3 5)
。
(funcall *my-nums-f*)
这里我们调用存储在*my-nums-f*
中的函数,即(lambda () (format t "~a~%" *nums*))
。此函数取 *nums*
的当前值,即 '(2 3 5)
.
-> (2 3 5)
-> NIL
您似乎期望 lambda
以某种方式内联函数主体中使用的所有变量的当前值,这确实会在该点导致 (format t "~a~%" '(1 2 3))
。
但这不是它的工作原理:函数引用变量本身,而不是变量值的快照。这就是为什么该函数在调用时会看到 *nums*
的当前值。
这是模拟动态变量的另一种外观。假设 NUMS 是在调用 BAR 之前绑定到 '(2 3 5) 的动态变量。 NUMS 在 lambda 中是免费的,并且是封闭的。但是,在 lambda returns 之前,NUMS 是反弹。这就是当动态 var 超出将新绑定推送到它的小值堆栈的词法范围时发生的情况。
(defun bar ()
(let ((nums '(1 2 3)))
(prog1
(lambda () nums)
(setf nums '(2 3 5)))))
SBCL 会告诉您它是否创建了闭包:#<FUNCTION ...>
与 #<CLOSURE ...>
。您的代码不会创建闭包,因为 lambda 主体中没有自由变量。