Question

I'm trying to wrap my head around Elisp's cl-loop facility but can't seem to find a way to skip elements. Here's an artificial example to illustrate the problem: I'd like to loop over a list of integers and get a new list in which all odd integers from the original list are squared. The even integers should be omitted.

According to the documentation of cl-loop, I should be able to do this:

(loop for i in '(1 2 3)
      if (evenp i)
        append (list)
      else
        for x = (* x x)
        and append (list x))

The desired output is '(1 9) instead I get an error:

cl--parse-loop-clause: Expected a `for' preposition, found (list x)

Apparently the and doesn't work as expected but I don't understand why. (I'm aware that I could simplify the else block to consist of only one clause such that the and isn't needed anymore. However, I'm interested in situations where you really have to connect several clauses with and.)

Second part of the question: Ideally, I would be able to write this:

(loop for i in '(1 2 3)
      if (evenp i)
        continue
      for x = (* x x)
      append (list x))

Continue is a very common way to skip iterations in other languages. Why doesn't cl-loop have a continue operator? Is there a simple way to skip elements that I overlooked (simpler than what I tried in the first example)?

Was it helpful?

Solution

In Common Lisp it is not possible to write such a LOOP. See the LOOP Syntax.

There is a set of variable clauses on the top. But you can't use one like FOR later in the main clause. So in an IF clause you can't use FOR. If you want to introduce a local variable, then you need to introduce it at the top as a WITH clause and set it later in the body.

(loop for i in '(1 2 3)
      with x
      if (evenp i)
        append (list)
      else
        do (setf x (* i i))
        and append (list x))

LOOP in Common Lisp also has no continue feature. One would use a conditional clause.

Note, that Common Lisp has a more advanced iteration construct as a library ITERATE. It does not exist for Emacs Lisp, though.

OTHER TIPS

You could do:

(loop for i in '(1 2 3)
      if (oddp i) collect (* i i))

That would solve your sample problem.

And here's another without loop (yes, I know you asked for loop):

(let ((ns  ()))
  (dolist (n  '(1 2 3))
    (when (oddp n) (push (* n n) ns)))
  (nreverse ns))

And without even cl-lib (which defines oddp):

(let ((ns  ()))
  (dolist (n  '(1 2 3))
    (unless (zerop (mod n 2)) (push (* n n) ns)))
  (nreverse ns))

Everything about such definitions is clear -- just Lisp. Same with @abo-abo's examples.

loop is a separate language. Its purpose is to express common iteration scenarios, and for that it can do a good job. But Lisp it is not. ;-) It is a domain-specific language for expressing iteration. And it lets you make use of Lisp sexps, fortunately.

(Think of the Unix find command -- similar. It's very handy, but it's another language unto itself.)

[No flames, please. Yes, I know that dolist and all the rest are essentially no different from loop -- neither more nor less Lisp. But they are lispier than loop. Almost anything is lispier than loop.]

Here's a loop solution:

(loop for i in '(1 2 3)
   when (oddp i) collect (* i i))

Here's a functional solution:

(delq nil
      (mapcar (lambda(x) (and (oddp x) (* x x)))
              '(1 2 3)))

Here's a slightly different solution (be careful with mapcan - it's destructive):

(mapcan (lambda(x) (and (oddp x) (list (* x x))))
        '(1 2 3)) 
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top