Unexpected behavior with loop macro and closures

Why do these forms behave this way?

CL-USER> (setf *closures* (loop for num in (list 1 2 3 4) collect (lambda () num))) ( #<COMPILED-LEXICAL-CLOSURE #x302004932E1F> #<COMPILED-LEXICAL-CLOSURE #x302004932DCF> #<COMPILED-LEXICAL-CLOSURE #x302004932D7F> #<COMPILED-LEXICAL-CLOSURE #x302004932D2F>) CL-USER> (funcall (first *closures*)) 4 CL-USER> (funcall (second *closures*)) 4 

I would expect the first funcall to return 1, and the second to return 2, etc. This behavior is consistent with both the implementation of Clozure Common Lisp and Steel-Bank Common Lisp.

If I recycle the loop macro in the version using dolist, then what I expect will be returned:

 (setf *closures* (let ((out)) (dolist (item (list 1 2 3 4) (reverse out)) (push (lambda () item) out)))) ( #<COMPILED-LEXICAL-CLOSURE #x302004A12C4F> #<COMPILED-LEXICAL-CLOSURE #x302004A12BFF> #<COMPILED-LEXICAL-CLOSURE #x302004A12BAF> #<COMPILED-LEXICAL-CLOSURE #x302004A12B5F>) CL-USER> (funcall (first *closures*)) 1 CL-USER> (funcall (second *closures*)) 2 

CL-USER>

What happens to the loop macro version?

+4
source share
2 answers

num is the same variable shared by all lambdas.

Using

 (setf *closures* (loop for num in (list 1 2 3 4) collect (let ((num1 num)) (lambda () num1)))) 

num1 is a new variable for each iteration.

As in the case of dolist , "depends on the implementation: does the dolist add a new var binding at each iteration or does it establish a binding for var once at the beginning, and then assigns it to any subsequent iterations". (CLHS, Macro DOLIST). That way, he can work on one implementation and crash another.

+8
source

The name num represents the same binding during the LOOP evaluation. Perhaps you want to write:

 (mapcar 'constantly (list 1 2 3 4)) 

to understand what you mean.

+4
source

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


All Articles