Search code examples
functional-programminglispcommon-lispmap-function

Applying append on mapcon in Lisp


The Lisp function G is defined by:

(defun g (l)
   (mapcon #'list l)) 

What is the result of evaluating the form (apply #'append (mapcon #'g '(1 2)))?
Justify the answer.

I've seen that mapcon works with nconc and cdr, but the final answer will be (1 2 2 2) and I don't know how to properly explain it. Please help.


Solution

  • First, let's call:

    (mapcon (lambda (list) (print list) nil) '(1 2))
    

    Because the anonymous function returns NIL all the time, the resulting list is NIL; the call prints this:

    (1 2) 
    (2) 
    

    So in your example, when you call (mapcon #'g '(1 2)), g will be called first with (1 2), then with (2). Function g returns a list, and mapcon concatenates them.

    What happens can be replicated in the REPL by computing each part explicitly:

    USER> (mapcon #'list '(1 2))
    ((1 2) (2))
    
    USER> (mapcon #'list '(2))
    ((2))
    
    USER> (nconc ** *)
    ((1 2) (2) (2))
    

    Finally, (apply #'append list-of-lists) calls append with a list of arguments.

    The signature of append is:

    append &rest lists => result 
    

    Which means that if l1, l2 and l3 are lists, the list that contains all their elements is:

    (append l1 l2 l3)
    

    Here the arguments to append are stored in a list, so the way to pass an arbitrary list of arguments to a function is by using apply. So that means (apply #'append lists) concatenates all the lists in lists, which is why in your case the result is (1 2 2 2).

    Note that using apply is not recommended when the number of arguments is arbitrary (possibly large), since apply is limited by CALL-ARGUMENTS-LIMIT. One other possible way to do it is:

    (loop for list in lists append list)