Here's two alternatives to loop you can use when you need to
both collect into and nconc onto a fresh list:
collecting along with an iteration macro (here,
dolist).
collect, wrap the value in a list. To “simulate” nconc,
simply return the value. To collect and/or nconc multiple values in
one iteration, use the normal list operations (nconc,
append, backquote, etc.) to build up the list.
Here, the mapcan alternative is not actually using anything new
provided by Loopless. The standard already provides suitable
alternatives to loop for many scenarios; Loopless comes to the
rescue when that isn't the case.
(collecting
(dolist (element '(a 24 x y 86 "test" (nested stuff)))
(typecase element
(symbol (collect element))
(number (collect (- element)))
(list (ncollect (copy-seq element))))))
==
(mapcan (lambda (element)
(typecase element
(symbol (list element))
(number (list (- element)))
(list (copy-seq element))))
'(a 24 x y 86 "test" (nested stuff)))
==
(loop for element in '(a 24 x y 86 "test" (nested stuff))
if (symbolp element)
collect element
else if (numberp element)
collect (- element)
else if (listp element)
nconc (copy-seq element))
⇒ (A -24 X Y -86 NESTED STUFF)
Here's how to use with-collectors to collect into different
lists simultaneously. Notice how we can use etypecase because
we don't have to conform to loop's syntax (it's true that
iterate, an alternative to loop, doesn't have that
problem).
(with-collectors (symbols numbers strings)
(dovector (element #(a 24 b "test" 86 "this" c))
(etypecase element
(symbol (symbols element))
(number (numbers element))
(string (strings element)))))
==
(loop for element across #(a 24 b "test" 86 "this" c)
if (symbolp element)
collect element into symbols
else if (numberp element)
collect element into numbers
else if (stringp element)
collect element into strings
finally (return (values symbols numbers strings)))
⇒
(A B C)
(24 86)
("test" "this")