💾 Archived View for thrig.me › blog › 2022 › 12 › 16 › DOLIST-pitfall.gmi captured on 2023-07-10 at 14:07:59. Gemini links have been rewritten to link to archived content
⬅️ Previous capture (2023-04-19)
-=-=-=-=-=-=-
Common LISP expressions return a value,
SBCL> 42 42 SBCL> (+ 3 2 1) 6 SBCL> (if (> 0.5 (random 1.0)) 'heads 'tails) HEADS SBCL> (let ((x 42)) x) 42
with complications such as PROG1 which returns the first-form.
SBCL> (prog1 99 640 42) 99
This is handy to yield something and then modify that something.
SBCL> (let ((x 0)) (defun counter () (prog1 x (incf x)))) COUNTER SBCL> (counter) 0 SBCL> (counter) 1
Many languages have to make do with a barbaric tmp = x; x++; return tmp dance. Gross!
I've seen multiple people trip over the fact that DOLIST does not yield what one might reasonably expect.
(dolist (x '(1 2 3)) (format t "~&~a" x) x)
This returns NIL, not 3--surprise!
SBCL> (dolist (x '(1 2 3)) (format t "~&~a" x) x) 1 2 3 NIL
The return value is the optional third argument following the list-form, here '(1 2 3). However, this cannot be the first argument.
SBCL> (dolist (x '(1 2 3) x) (format t "~&~a" x)) 1 2 3 NIL SBCL> (dolist (x '(1 2 3) 'i-want-x-here) (format t "~&~a" x)) 1 2 3 I-WANT-X-HERE
There is LOOP, but many folks seem hesitant to use it. Dunno why.
SBCL> (loop for x in '(1 2 3) do (format t "~&~a" x) finally (return x)) 1 2 3 3
Sometimes a list is being built up, which LOOP is good at, but again folks are often hesitant to use LOOP. Dunno why. Assume that '(1 2 3) is actually some complicated API call, if that makes more sense than using LOOP to make '(1 2 3) from '(1 2 3).
SBCL> (loop for x in '(1 2 3) collect x) (1 2 3)
This can be done with DOLIST, but I would probably write it with LOOP. The LOOP is probably generating very similar code to what the following probably also generate.
SBCL> (let (foo) (dolist (x '(1 2 3) (nreverse foo)) (push x foo))) (1 2 3) SBCL> (let (foo) (dolist (x '(1 2 3)) (push x foo)) (nreverse foo)) (1 2 3)
Given PROG1, guess what PROG2 does. (Common LISP suffers from having too many functions and not enough; there's bloat that probably could be shuffled off to a library or dropped, whoops about that backwards compatibility, and then other folks complain that HTTP (or even socket) support is not in base...)
The peanut gallery indicates that LOOP is unloved as it uses a domain specific language and not SEXP--LISP purism. Another reason it is unloved is that some noobs look at LOOP and struggle with the "oh no, another thing to learn" problem.
But there is not much reason to ITERATE this more.
Another fun thing is to write your own looping macros...
tags #lisp