If we assign a value to a variable:
(setf i 10)
and then a create a lambda function closing over it:
(setf f #'(lambda () i))
We have the behavior
(incf i) ;=> 11
(funcall f) ;=> 11
Instead, I would like the function to always return the value of i
at the time that the function was created. E.g.:
(incf i) ;=> 11
(funcall f) ;=> 10
Essentially i would like to turn i
into a literal inside the lambda body. Is this possible to do in Common Lisp? The reason is that I'm creating more than one lambda inside a loop, and need to use the index in their bodies, without them varying after creation.
Just bind a variable with a copy of the value. E.g.:
(let ((i i))
(lambda () i))
This is actually an important technique with iteration constructs, because something like
(loop for i from 1 to 10
collecting (lambda () i))
may return ten closures over the same variables, so it becomes necessary to write:
(loop for i from 1 to 10
collecting (let ((i i)) (lambda () i)))
If you really only need a function that returns the value, you could also use constantly (but I expect the real use case is more complicated):
(loop for i from 1 to 10
collecting (constantly i))
The ambiguity in iteration forms is actually specified by the standard, in some cases. E.g., for dotimes, dolist
It is implementation-dependent whether dotimes establishes a new binding of var on each iteration or whether it establishes a binding for var once at the beginning and then assigns it on any subsequent iterations.
The more primitive do, however, actually specifies that there is one set of bindings for the form, and that they are updated at each iteration (emphasis added):
At the beginning of each iteration other than the first, vars are updated as follows. …
This ambiguity gives implementations a bit more flexibility. Dolist, for instance could be defined with either of the following:
(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)))
It's not entirely clear what you want here. If you want to create a scope within which there exists a shared variable i
, you can do that with a 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>
If you're looking to use dynamically scoped variables, you can use straight-up 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>
Just in case the above two answers don't suffice in clarity:
(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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With