如果我们为变量赋值:
(setf i 10)
然后创建一个关闭它的lambda函数:
(setf f #'(lambda () i))
我们有行为
(incf i) ;=> 11
(funcall f) ;=> 11
相反,我希望函数在创建函数时始终返回i
的值。 E.g:
(incf i) ;=> 11
(funcall f) ;=> 10
基本上我想将i
转换为lambda体内的文字。这可以在Common Lisp中完成吗?原因是我在一个循环中创建了多个lambda,并且需要在它们的主体中使用索引,而不会在创建后改变它们。
答案 0 :(得分:8)
只需将变量与值的副本绑定即可。 E.g:
(let ((i i))
(lambda () i))
这实际上是迭代结构的一项重要技术,因为像
这样的东西(loop for i from 1 to 10
collecting (lambda () i))
可能会在相同的变量上返回10个闭包,因此有必要写:
(loop for i from 1 to 10
collecting (let ((i i)) (lambda () i)))
如果你真的只需要一个返回值的函数,你也可以使用constantly(但我希望实际用例更复杂):
(loop for i from 1 to 10
collecting (constantly i))
在某些情况下,迭代表单中的歧义实际上是由标准指定的。例如,dotimes,dolist
dotimes是否在每次迭代时建立var的新绑定,或者它是否在开始时为var建立一次绑定,然后在任何后续迭代中分配它,这是依赖于实现的。
然而,更原始的do实际上指定了表单的一组绑定,并且在每次迭代时都会更新它们(强调添加):
在第一次以外的每次迭代开始时,vars按更新,如下所示。 ...
这种模糊性使实现更具灵活性。例如, Dolist 可以使用以下任一方式定义:
(defmacro dolist ((var list &optional result) &body body)
`(progn (mapcar #'(lambda (,var)
,@(ex:body-declarations body)
(tagbody
,@(ex:body-tags-and-statements body)))
,list)
(let ((,var nil))
,result)))
(defmacro dolist ((var list &optional result) &body body)
(let ((l (gensym (string '#:list-))))
`(do* ((,l ,list (rest ,l))
(,var (first ,l) (first ,l)))
((endp ,l) ,result)
,@body)))
答案 1 :(得分:4)
在这里你想要的并不完全清楚。如果您想创建一个存在共享变量i
的范围,您可以使用let
执行此操作。
CL-USER> (let ((i 10))
(defun show-i () i)
(defun inc-i () (incf i))
(defun dec-i () (decf i)))
DEC-I
CL-USER> (show-i)
10
CL-USER> (inc-i)
11
CL-USER> (show-i)
11
CL-USER> (dec-i)
10
CL-USER> (dec-i)
9
CL-USER> (show-i)
9
CL-USER>
如果您希望使用动态范围变量,可以直接使用defvar
。
CL-USER> (defvar *a* 10)
*A*
CL-USER> (defun show-a () *a*)
SHOW-A
CL-USER> (show-a)
10
CL-USER> *a*
10
CL-USER> (incf *a*)
11
CL-USER> (incf *a*)
12
CL-USER> (show-a)
12
CL-USER>
答案 2 :(得分:4)
以防上述两个答案不够明确:
(defparameter *i* 10)
;;Before you modify *i*
(defvar f (let ((i *i*))
#'(lambda () i)))
;;Now f will always return 10
(funcall f) => 10
(incf *i*) => 11
(funcall f) => 10