In my day instead of (loop for a in l appending (g a))
we wrote (mapcan #'g l)
. Which is equivalent to (apply #'append (mapcar #'g l))
, more or less:
(defun flatten (l)
(if l
(if (atom l)
(list l)
(mapcan #'flatten l))))
So what does it mean in this case? Imagine you call (flatten (list 1 2 3 4 5))
, i.e. the argument list has only atoms in it. Each atom in that list gets enclosed in a list -- becomes a singleton list, like (1) (2)
etc. Then they are all appended together, giving us back ... the original list:
( 1 2 3 4 5 )
( (1) (2) (3) (4) (5) )
( 1 2 3 4 5 )
So flattening a list of atoms is an identity operation (in Common LISP, that's #'identity
). Now imagine flattening a list which has some atoms in it as well as a list of atoms. Again, each element of the list gets transformed by flatten
and then they are all appended together. A list of atoms stays as itself, as we just saw. Atoms get enclosed each in a list. So appending will give us back all the atoms that were on both levels in the nested list, now flattened:
( 11 12 (1 2 3 4) 13 )
( (11) (12) (1 2 3 4) (13) )
( 11 12 1 2 3 4 13 )
And so on and so forth, for more levels of nesting as well.
NIL
s as elements in lists pose a problem. NIL
is an empty list, and empty list contains nothing, so should not contribute anything. But NIL
is also an atom. So we make a special case for it, to not enclose it in a singleton list - leave it as it is, so when appended, it'll just disappear.