Search code examples
loopslispcommon-lisp

How can I loop with more than 1 variable at once in Common Lisp?


So, I can loop with one variable like this:

(loop for i in '(1 2 3 4 5) do (print i))

I can get 1 number and bind it to i.

But how can I bind to more than 1 variable at once, e.g. something like:

for i,j,k in '(1 2 3 4 5)
-> i = 1, j = 2, k = 3

I already tried searching around but only found with statement to define more variables, but it does not allow me to bind the variables directly.


Solution

  • To elaborate on the other answer. If you simply wish to iterate with multiple variables then then you can do this by

    (loop for x ...
          for y ...
          do ...)
    

    This does sequential binding: when y is stepped, x has already been stepped. Parallel binding happens with and:

    (loop for x ...
          and y ...
          do ...)
    

    If you want to destructure a single list, then you can use destructuring. But the destructuring loop does is kind of horrible, and in particular it is not, of course, compatible with the destructuring done b destructuring-bind or (equivalently) macro lambda lists. In particular as the other answer shows (a b c) does match (1 2 3 4 5 6), or indeed ().

    However the other answer is probably unsafe in general. Consider this function:

    (defun foo (l)
      (loop for (a b c) on l by #'cddr
            while c
            do (print (list a b c))))
    

    Well now consider (foo '(1 2 nil 3 4 5)).

    A better approach is this, which both avoids the worst horrors of loop's destructuring, and gets the termination condition closer to right, as well as being, probably, slightly more efficient:

    (defun foo (l)
      (loop for (a b c . tail) = l then tail
            do (print (list a b c))
            while tail))
    

    Now

    > (foo '(1 2 nil 3 4 5))
    
    (1 2 nil) 
    (3 4 5) 
    nil