Capturing variable value when creating lambda

If we assign a value to a variable:

(setf i 10) 

and then create a lambda function above it:

 (setf f #'(lambda () i)) 

We have behavior

 (incf i) ;=> 11 (funcall f) ;=> 11 

Instead, I would like the function to always return the value of i when the function was created. For instance:.

 (incf i) ;=> 11 (funcall f) ;=> 10 

Essentially, I would like to turn i into a literal inside the body of the lambda. Can this be done in Common Lisp? The reason is because I create more than one lambda inside the loop and you need to use the index in their bodies, without changing them after creation.

+5
source share
3 answers

Just bind the variable with a copy of the value. For instance:.

 (let ((ii)) (lambda () i)) 

This is a really important method with iterative constructs, because something like

 (loop for i from 1 to 10 collecting (lambda () i)) 

can return ten closures to the same variables, so it becomes necessary to write:

 (loop for i from 1 to 10 collecting (let ((ii)) (lambda () i))) 

If you really need a function that returns a value, you can also use constantly (but I expect the real use case is more complicated):

 (loop for i from 1 to 10 collecting (constantly i)) 

Uncertainty in the forms of iterations is in fact determined by the standard, in some cases. For example, for dotimes , dolist

This is implementation dependent, whether the dotimes sets a new var binding at each iteration or whether it sets the binding for var once at the beginning and then assigns it to any subsequent iterations.

The more primitive do , however, actually indicates that there is one set of bindings for the form and that they are updated at each iteration (highlighted by me):

At the beginning of each iteration other than the first, the vars are updated as follows. & Hellip;

This ambiguity gives implementations a bit more flexibility. A dollist , for example, can be determined using one of the following actions:

 (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))) 
+8
source

It is not clear what you want here. If you want to create a region in which the shared variable i exists, you can do this with 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 want to use dynamically modified variables, you can use the direct value 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> 
+4
source

Just in case, these two answers are not enough for 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 
+4
source

Source: https://habr.com/ru/post/1206067/


All Articles