4

I want to be able to collect multiple lists inside a loop.

I know this can be done without a loop, however I was wondering whether this would be possible with a loop as well. I'd like something like this:

  (loop for var in list
        (if (cond1 var)
            (if (cond2 var)
                collect into list1
                collect into list2))
        finally (list list1 list2))

I get the error that the LOOP keyword is expected, I guess the collect should be used right after a when or a loop. Is there any way to solve that?

Rainer Joswig
  • 136,269
  • 10
  • 221
  • 346
Kevin Van Ryckegem
  • 1,915
  • 3
  • 28
  • 55

2 Answers2

7

Believe it or not you have too many parentheses. loop has its own little sublanguage which is quite different from Common Lisp s-expressions.

(loop :for var :in list
      :when (cond1 var)
        :if (cond2 var)
           :collect var :into list1
        :else
           :collect var :into list2
        :end
      :end
      :finally (return (list list1 list2)))

In addition I specify what to :collect and :finally just evaluates so you need to use (return ...) or else you can expect nil.

I use keywords for loop keywords to distinguish them from other symbols. It's just style. The indentation is of course ignored and just for readability, but :end isn't. However in this case both :end keywords are redundant since the loop is parsed correctly without them.

I'm by no means a loop wiz.. Every time I get something slightly complex I tend to use Land of Lisp's Periodic table or Loop for black belts, a chapter from Practical Common Lisp.

Periodic table of Loop Macro

Svante
  • 50,694
  • 11
  • 78
  • 122
Sylwester
  • 47,942
  • 4
  • 47
  • 79
  • Could you share the source of the "Periodic Table of the Loop Macro" please? – Hellseher Dec 24 '20 at 22:33
  • @Hellseher As I wrote in my answer it's from the book [Land of Lisp](http://www.landoflisp.com/). There is a PDF version on the Internet however as this is copyrighted work I don't think that would be considered fair use to put on SO. I own a copy and urge everyone interested to buy the book as it is a great work of art. – Sylwester Dec 25 '20 at 01:13
6

You have a couple of problems.

First, if you want to put normal Lisp code into the loop body, you need the do keyword. But you could use do's when or if keywords instead.

Second, the collect keyword requires you to specify an expression to collect into the list, e.g. collect var into list1.

Third, you need to use (return expression) to return a value from the form in the finally clause.

  (loop for var in list
    when (cond1 var)
      if (cond2 var)
        collect var into list1
      else
        collect var into list2
    finally (return (list list1 list2)))
Barmar
  • 741,623
  • 53
  • 500
  • 612