Quoting from the standard:
mapcan
and mapcon
are like mapcar
and maplist
respectively, except that the results of applying function are combined into a list by the use of nconc
rather than list
.
So, does that mean mapcar
must be implemented by (apply #'list ...)
? Don't be silly, of course it need not be. What it means is that the results from the function called by mapcar
are combined into a list in the same way list
does that: by building a chain of cons cells whose cars point to the returned values.
In exactly the same way, the results returned from the function called by mapcan
are combined in the way that nconc
does, namely that they must be lists and these lists are concatenated destructively.
That is what the language in the standard means.
As an example here is an implementation of a simple version (only one list argument) of mapcan
which does use nconc
to build the list, but only ever calls nconc
with two arguments. This is not how you would implement mapcan
in real life, but it is simple enough that you can see what is going on without relying on reduce
.
(defun mc (f l)
(labels ((mc-loop (tail head last)
;; tail is the rest of the list we're processing, head is
;; the thing we are building, last is the last non-null
;; return from f if there is one
(if (null tail) ;we're done
head
(let ((r (funcall f (first tail))))
(mc-loop (rest tail)
(if (null head) r head) ;the first non-null return is the head
(if (null r)
;; last non-null element is unchanged
last
(progn
;; smash last and then r is the new last
(nconc last r)
r)))))))
(mc-loop l '() '())))
mapcan
is more efficient than a straightapply
; thenconc
comment is merely to show the difference betweenmapcan
and the other list functions. – Voltaic