help-gnu-emacs
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: `append' vs. `nconc'


From: Pascal J. Bourguignon
Subject: Re: `append' vs. `nconc'
Date: Wed, 30 Dec 2015 17:18:32 +0100
User-agent: Gnus/5.13 (Gnus v5.13) Emacs/24.3 (gnu/linux)

Emanuel Berg <embe8573@student.uu.se> writes:

> Teemu Likonen <tlikonen@iki.fi> writes:
>
>> There you create a list MODES and then APPEND copies
>> the whole list and joins it to AUTO-MODE-ALIST.
>> The original list is discarded. No problem, it's
>> just a configuration code. But in some other
>> situation it might be good idea to construct the
>> list only once and use NCONC which only traverses
>> the lists through and modifies last conses to join
>> it to the next list:
>>
>>     (let ((modes (list '(a . b) ;; ... ))) (setq
>> auto-mode-alist (nconc modes auto-mode-alist)))
>
> So what you are saying, if `append' is replaced by
> `nconc', the same thing happens, only one less list
> has to be created?
>
> I have several appends in my source, is there
> a rule-of-thumb when to use `append' and when to use
> `nconc'?

In doubt, use append.

If you are appending freshly consed lists, then it's safe to use nconc.

For example:

    (defun f (lists)
      (loop
        for list in lists
        append list))

    (defun f* (lists)
      (apply (function append) lists))

    (defun g (lists)
      (loop
         for list in lists
         nconc (mapcar (function 1+) list)))


In f, we are appending lists that are given to the function in the
parameter. Since we don't know whether those lists will be shared or
literal (we didn't specify anything about the lists), we should avoid
mutating them, therefore we use append in f.  A new copy of each of the
list will be used to build the result.

Notice that in f*, append does not copy the last list. We obtain a
result that shares structure with the arguments.  This may be a problem
or not, but in any case, that means that the result of f* is not a fresh
list, even if most of it is made of new cons cells, because of this
shared tail!

When you have a fixed set of lists to append, you can either use
concatenate, or add a '() as last argument to append:

        (concatenate 'list l1 l2 l3) --> a fresh list
        (append l1 l2 l3 '())        --> a fresh list
        (append l1 l2 l3)            --> a list sharing structure with l3.


In the case of g, since we create new lists (by way of mapcar), there
are no other reference to those lists, so we can use nconc, mutating
their last cdr to concatenate them without having to copy them.



Another way to say it: if you could use sort on your lists without
copy-list, then you can use nconc, otherwise you should use append:

(let ((l (list 1 2 3))
       s)
  (list (setf s (sort l '<))     ; ok
        (nconc s (list 4 5 6)))) ; ==> nconc ok

(let ((l '(1 2 3))
      s)
  (list (setf s (sort (copy-list l) '<))  ; copy-list needed
        (append l (list 4 5 6))           ; ==> use append
        (nconc s (list 4 5 6)))) ; s is a fresh copy ==> nconc ok


-- 
__Pascal Bourguignon__                 http://www.informatimago.com/
“The factory of the future will have only two employees, a man and a
dog. The man will be there to feed the dog. The dog will be there to
keep the man from touching the equipment.” -- Carl Bass CEO Autodesk


reply via email to

[Prev in Thread] Current Thread [Next in Thread]