Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Common Lisp Loop

In the following loop:

(let ((funs (loop for i upto 3 do (print i) collect #'(lambda () i))))
  (loop for fun in funs collect (funcall fun)))

i would intuitively think i would get a list of four closures which return the numbers 0 1 2 and 3 upon being called, but this is what i get:

>> 0 
>> 1 
>> 2 
>> 3
=> (4 4 4 4)

But rebinding the i locally to something else:

(let ((funs (loop for i upto 3 do (print i) collect (let ((x i))
                          #'(lambda () x)))))
  (loop for fun in funs collect (funcall fun)))

works as expected:

>> 0 
>> 1 
>> 2 
>> 3
=> (0 1 2 3)

So each of the functions return 4, why are all return values the same, and why 4?

update

This seems to be a question about lambda actually. See below:

(setq dynamic-var 8
  funs ())
(push (lambda () dynamic-var) funs)
(incf dynamic-var)
(push (lambda () dynamic-var) funs)
(mapcar #'funcall funs)         ;(9 9)
like image 219
Student Avatar asked Jan 26 '23 12:01

Student


1 Answers

What does

(let (i      ; a counter variable
      f)     ; a list of functions
  (setf i 1)
  (push (lambda () i) f)
  (setf i 2)
  (push (lambda () i) f)

  (mapcar #'funcall f))

return?

How about:

(let (i
      f)
  (setf i 1)
  (push (lambda () i) f)
  (let (i)
    (setf i 2)
    (push (lambda () i) f))

  (mapcar #'funcall f))

Which is the LOOP model?

See also:

CL-USER 42 > (let (f)
               (dotimes (i 10 (mapcar #'funcall (reverse f)))
                 (push (lambda () i) f)))
(10 10 10 10 10 10 10 10 10 10)

CL-USER 43 > (let (f)
               (dotimes (i 10 (mapcar #'funcall (reverse f)))
                 (push (let ((i i))
                         (lambda () i))
                       f)))
(0 1 2 3 4 5 6 7 8 9)

The Common Lisp standard says for DOTIMES:

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.

you write:

i would intuitively think i would get a list of four closures which return the numbers 0 1 2 and 3 upon being called

This intuition is only partly correct. You get four closures, but in this case they all share one variable binding. Thus they can only see the current binding of this one variable. In Common Lisp this binding is mutable and closures see the current binding, not the one of the initial binding of closure creation time.

Your intuition would be right, when each closure had its own variable binding.

Additional answer: why is this Lisp returning 10 ?

(PROGN
  (SETQ I (THE INTEGER (1+ (THE INTEGER I))))
  (WHEN (>= (THE INTEGER I)
            (THE INTEGER #:|dotimes-count-1075|))
    (GO #:|dotimes-end-tag1080|)))

Above is a part of the macro expansion of the dotimes construct. As you can see it first increments the variable and then tests for >=. Thus it exits when I is >= 10. Thus the last value of I is 10. Later after exiting the dotimes you are retrieving the value of I and then it's 10.

like image 99
Rainer Joswig Avatar answered Jan 31 '23 10:01

Rainer Joswig