From: Manan Sanghi
Subject: nconc vs append
Date: 
Message-ID: <b4flbp$oaq$1@news.acns.nwu.edu>
Which of these is better?
(apply #'append '((1 2) (3 4) (4 312))) => (1 2 3 4 4 312)
or
(apply #'nconc '((1 2) (3 4) (4 312))) => (1 2 3 4 4 312) ?

From: Manan Sanghi
Subject: Re: nconc vs append
Date: 
Message-ID: <b4fs7d$st8$1@news.acns.nwu.edu>
Thank you all for your replies.

In the situation I am dealng with I have the list of lists generated for a
local variable which is not used anywhere else. Hence it appears safe to
modify it using nconc.

But your replies were very thorough and helpful.

Thanks once again!
From: Kaz Kylheku
Subject: Re: nconc vs append
Date: 
Message-ID: <cf333042.0303091241.3a7dc790@posting.google.com>
"Manan Sanghi" <·····@cs.nwu.edu> wrote in message news:<············@news.acns.nwu.edu>...
> Which of these is better?
> (apply #'append '((1 2) (3 4) (4 312))) => (1 2 3 4 4 312)
> or
> (apply #'nconc '((1 2) (3 4) (4 312))) => (1 2 3 4 4 312) ?

The one which won't make demons fly out of your nose, obviously!

You can't destructively operate on constant objects; you are
effectively writing self-modifying code. When the source code of a
Lisp expression contains a quoted object like '(1 2 3), that object is
essentially part of the body that expression. When that expression is
compiled, the translated image may contain a write-only copy of that
list, which is regurgitated whenever the evaluation of '(1 2 3) is
requested.

So any number of behaviors can happen. The self-modification may
succeed, so that next time what used to be the '(1 2 3) expression is
evaluated, the modified object is produced. Or the self-modification
may not happen, because it so happens in the given implementation that
'(1 2 3) actually conses up a new object each time just like (list 1 2
3). Or the program may simply crash your Lisp image; maybe the
compiled code is placed into a page of virtual memory that is marked
read-only. Or the Lisp system may detect the error and signal it using
a condition. Then there are those nasal demons.
From: Pierpaolo BERNARDI
Subject: Re: nconc vs append
Date: 
Message-ID: <nAJaa.38608$zo2.1162854@news2.tin.it>
"Manan Sanghi" <·····@cs.nwu.edu> ha scritto nel messaggio ·················@news.acns.nwu.edu...
> Which of these is better?
> (apply #'append '((1 2) (3 4) (4 312))) => (1 2 3 4 4 312)
> or
> (apply #'nconc '((1 2) (3 4) (4 312))) => (1 2 3 4 4 312) ?

It depends on whether you can safely modify the lists or not.

Note that your second example modifies constant lists,
so its effects are undefined.

P.
From: Gareth McCaughan
Subject: Re: nconc vs append
Date: 
Message-ID: <slrnb6mpf5.12d5.Gareth.McCaughan@g.local>
Manan Sanghi wrote:

>  Which of these is better?
>  (apply #'append '((1 2) (3 4) (4 312))) => (1 2 3 4 4 312)

This one is legal.

>  or
>  (apply #'nconc '((1 2) (3 4) (4 312))) => (1 2 3 4 4 312) ?

This one is illegal: it involves modifying a literal
object. See section 3.7.1 of the HyperSpec.

NCONC will typically run faster and generate less
garbage than APPEND, but if you use it as above
then "the consequences are undefined", which means
that your Lisp system is within its rights to wipe
your hard disc and send pornographic e-mails to
your boss.

NCONC is dangerous in other circumstances even
when it's legal, of course, because it modifies
the lists it acts on. Only use it when you can
prove it's safe.

-- 
Gareth McCaughan  ················@pobox.com
.sig under construc
From: Nils Goesche
Subject: Re: nconc vs append
Date: 
Message-ID: <87u1ech6w5.fsf@darkstar.cartan>
"Manan Sanghi" <·····@cs.nwu.edu> writes:

> Which of these is better?

> (apply #'append '((1 2) (3 4) (4 312))) => (1 2 3 4 4 312)

> (apply #'nconc '((1 2) (3 4) (4 312))) => (1 2 3 4 4 312) ?

The former.  In the latter example you are modifying a list
literal which you are not supposed to do.  Otherwise, if your
question is whether APPEND or NCONC is ``better��, no answer can
be given -- better for /what/?

Regards,
-- 
Nils G�sche
Ask not for whom the <CONTROL-G> tolls.

PGP key ID #xD26EF2A0
From: Kalle Olavi Niemitalo
Subject: Re: nconc vs append
Date: 
Message-ID: <877kb8cymp.fsf@Astalo.kon.iki.fi>
"Manan Sanghi" <·····@cs.nwu.edu> writes:

> Which of these is better?
> (apply #'append '((1 2) (3 4) (4 312))) => (1 2 3 4 4 312)
> or
> (apply #'nconc '((1 2) (3 4) (4 312))) => (1 2 3 4 4 312) ?

NCONC would modify the list (1 2) in order to concatenate (3 4)
to it.  However, the list is part of a literal object, so the
consequences of such modification are undefined per CLHS 3.7.1.
APPEND is the better choice in this case.

You could try this:

  (defun test-it ()
    (apply #'nconc '((1 2) (3 4) (4 312))))
  (test-it)
  (test-it) ; what happens?
From: Thomas A. Russ
Subject: Re: nconc vs append
Date: 
Message-ID: <ymibrzzp9zi.fsf@sevak.isi.edu>
"Manan Sanghi" <·····@cs.nwu.edu> writes:

> 
> Which of these is better?
> (apply #'append '((1 2) (3 4) (4 312))) => (1 2 3 4 4 312)
> or
> (apply #'nconc '((1 2) (3 4) (4 312))) => (1 2 3 4 4 312) ?

In the specific case of your example, APPEND is the better solution
because you are passing it a constant list.  You are not allowed by the
standard to destructively modify constants, and since NCONC is a
destructive operator, you could end up in trouble -- for example if the
compiler puts the constant list in a read-protected memory page.

The real answer (absent the constant issue) is that it depends on
whether you can safely destructively modify the lists you are being
given.  Assume the following:

(defun f (a b c)
  (nconc a b c))

(defvar *a* (list 1 2))    ;; Use LIST so we can destructively modify
(defvar *b* (list 3 4))
(defvar *c* (list 4 312))
(defvar *d* (f *a* *b* *c*))

;; The destructive version will give the following results,
;; which you may or may not want.

*d*  =>  (1 2 3 4 4 312)
*a*  =>  (1 2 3 4 4 312)
*b*  =>  (3 4 4 312)
*c*  =>  (4 312)


Normally one would only use NCONC on list structure that has been
freshly created and that is not referenced anywhere else.  (There may be
exceptions, but they are rare).

-- 
Thomas A. Russ,  USC/Information Sciences Institute          ···@isi.edu    
From: Thomas A. Russ
Subject: Re: nconc vs append
Date: 
Message-ID: <ymi8yv2ozzh.fsf@sevak.isi.edu>
···@sevak.isi.edu (Thomas A. Russ) writes:

> "Manan Sanghi" <·····@cs.nwu.edu> writes:
> 
> > 
> > Which of these is better?
> > (apply #'append '((1 2) (3 4) (4 312))) => (1 2 3 4 4 312)
> > or
> > (apply #'nconc '((1 2) (3 4) (4 312))) => (1 2 3 4 4 312) ?
> 
> In the specific case of your example, APPEND is the better solution
> because you are passing it a constant list.  You are not allowed by the
> standard to destructively modify constants, and since NCONC is a
> destructive operator, you could end up in trouble -- for example if the
> compiler puts the constant list in a read-protected memory page.
                                       ^^^^
                                      write

Putting data in a read-protected memory page would perhaps make sense
only for handling some very, very, very highly classified data :)



-- 
Thomas A. Russ,  USC/Information Sciences Institute          ···@isi.edu    
From: Steven M. Haflich
Subject: Re: nconc vs append
Date: 
Message-ID: <3E87AA44.6080601@alum.mit.edu>
Thomas A. Russ wrote:

> You are not allowed by the
> standard to destructively modify constants, and since NCONC is a
> destructive operator, you could end up in trouble -- for example if the
> compiler puts the constant list in a read-protected memory page.

You would be lucky if the system puts the constant in protected memory
so a clean exception is signalled if you try to modify a manifest
constant in your function definition.  It is more pernicious if the
compiler does not detect it, because then you are modifying your program
in ways you probably didn't intend and about which you cannot possibly
predict the behavior.(*)

You can try to explit this behavior, as I have done here, but you should
never write real code like this.  It is illegal and useful only as an
example of what not to do.

<1> (defun accumulate (item)
       (cdr (nconc '(nil) (list item))))
accumulate
<2> (accumulate 1)
(1)
<3> (accumulate 2)
(1 2)
<4> (accumulate 3)
(1 2 3)
<5> (function-lambda-expression #'accumulate)
(lambda (item) (block accumulate (cdr (nconc '(nil 1 2 3) (list item)))))
nil
accumulate

(*) The reason you cannot predict the behavior of code that modifies
function constants is the the compiler is _permitted_ but not required
to share structure with a single file compilation even for structure
that is equal but not eq.  For example, if there were definitions of
accumulate-1 and accumulate-2 defined in a single file similar to the
definition above, they _might_ share the same constant list (nil).