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

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

Re: Why doesn't nconc change my variable?


From: Marcin Borkowski
Subject: Re: Why doesn't nconc change my variable?
Date: Sun, 05 Oct 2014 11:54:24 +0200

On 2014-10-05, at 03:58, Drew Adams wrote:

>> (setq my-list ())
>> (nconc my-list '("wtf"))
>> 
>> and my-list is still nil.  If, OTOH, I do
>> (setq my-list ())
>> (setq my-list (nconc my-list '("wtf")))
>> 
>> my-list is ("wtf").
>> 
>> Why is that so?  I though nconc is supposed to change all its
>> arguments but the last one.  Is the latter construct a correct
>> way of adding an element at the end of the list?
>
> No, it's not supposed to do that. You discovered just what it
> does do.  And you could have discovered it earlier by reading
> some doc.  See the Elisp manual, node `Rearrangement' (found by
> doing `i nconc' in the manual).

I did read it.  And a few times, for that matter.

You see, one of the worst intellectual mistakes you can make is to have
a wrong mental model of something.  This seems to be the case here, and
that's why I really want to grok this issue.

> See also node `Sets and Lists', in particular this (about `delq',
> but the same idea applies to other destructive list operations):
>
>  Note that `(delq 'c sample-list)' modifies `sample-list' to splice
>  out the third element, but `(delq 'a sample-list)' does not splice
>  anything--it just returns a shorter list.  Don't assume that a variable
>  which formerly held the argument LIST now has fewer elements, or that
>  it still holds the original list!  Instead, save the result of `delq'
>  and use that.  Most often we store the result back into the variable
>  that held the original list:
>
>      (setq flowers (delq 'rose flowers))

That I didn't read earlier; I've read it now, and still don't get it.
Sorry.  (Edit: while I was writing the rest of this post, things
apparently got more clear, but I'll leave the rest, since if I'm right,
this might be (hopefully) instructive for others reading it, and I might
even want to save it to post it on my blog.)

Now I have two choices.  One is to install the C source and try to read
it.  This I cannot do now (time constraints, and while I did learn some
C, it was, what, fifteen years ago or so).  That's why I'm asking here.
Please bear with me.  This should also be good for Emacs, since it may
be the case that if I cannot understand the manual, someone else might
have troubles with that, too, so maybe the manual is buggy.

What I thought was essentially this: under the hood, a list is a pointer
(using C terminology) to a cons cell.  The car of the cons cell is a
pointer to some data (say, a string), and the cdr - a pointer to the
next cons cell.

What I thought is that if you say, e.g.,

(setq my-list ("hello" "world"))

and then

(nconc my-list '("wtf"))

then my-list points to the list ("hello" "world" "wtf").  This seems to
be indeed the case.  (And the wtf's get added if I repeat the nconc step
- I confirmed that experimentally.)

Now, when I say

(setq my-list (list))

(or (setq list ()))

then nconc'ing '("wtf")'s onto it (as before) does not have the same
effect.

Why on earth is that!?

Does it mean that Pascal's earlier response was right?  Now that I'm
thinking about it, this makes sense: an empty list is (I would guess) a
NUL pointer, so nconc'ing has not the meaning of "changing the cdr" but
rather of "returning a brand new cons cell".  This in turn means that
this sentence from the manual:

"Instead, the last CDR of each of the LISTS is changed to refer to the
following list."

is misleading: if I have an empty list, there's no cdr to change, and
that's why nconc would (probably) branch to a different subroutine and
create a brand new cons cell.

(Now that I'm wondering what the implementation might actually be, I'm
more and more tempted to look at the C source.  This is probably a good
sign.)

So, now I have some more questions and one suggestion.  (More knowledge
always brings more questions, right?)

1. Do I get it correctly that the "destructiveness" of some functions
(like nconc) does /not/ mean that they /change/ some of their arguments,
but only that they /may/ do it, and that relying on that is risky?

2. Do I get it correctly that what Pascal called "a nil symbol" is just
NUL under the hood?  Does it mean that there's no difference between (),
nil and the result of (list) (as opposed to the results of calling list
twice with the same arguments)?  (They seem to be `eq', as I've just
checked.)

3. What is the "canonical" way to append an element to a list, in the
sense that I have a variable containing (=pointing to?) a list, I want
it to point to a list one element longer?  And, while at that, what is
the "canonical" way to /prepend/ an element to a list (is it `push'?)

4. (This is the suggestion.)  If the above is right, could someone make
it more clear in the manual (or point me to the place in the manual
where it is actually stated)?

TIA,

-- 
Marcin Borkowski
http://octd.wmi.amu.edu.pl/en/Marcin_Borkowski
Adam Mickiewicz University



reply via email to

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