From: Josip Gracin
Subject: Changing value of a list variable passed as argument to function
Date: 
Message-ID: <e5mest$5ab$1@sunce.iskon.hr>
Hello!

What's the proper way to pass a list variable to a function and have 
that variable changed in a way 'push' macro would change it?  For 
example, take a look at this code which isolates the problem.

(defun doit ()
   (let ((v (list 1 2 3)))
     (let ((x v))
       (push 5 x)    ; <-- here
     v)))

Function doit returns (1 2 3) and I believe I do understand why 
(symbols, objects, places etc.).  Is there a simple way to make this 
code return (5 1 2 3) by only changing the marked line?

From: Zach Beane
Subject: Re: Changing value of a list variable passed as argument to function
Date: 
Message-ID: <m3verldtx2.fsf@unnamed.xach.com>
Josip Gracin <······@tel.fer.hr> writes:

> Hello!
> 
> What's the proper way to pass a list variable to a function and have
> that variable changed in a way 'push' macro would change it?

A function can't do what PUSH does; PUSH manipulates a binding, while
functions only have values to work with. (How would it handle the list
NIL?)
 
> For example, take a look at this code which isolates the problem.
>
> (defun doit ()
>    (let ((v (list 1 2 3)))
>      (let ((x v))
>        (push 5 x)    ; <-- here
>      v)))
> 
> Function doit returns (1 2 3) and I believe I do understand why
> (symbols, objects, places etc.).  Is there a simple way to make this
> code return (5 1 2 3) by only changing the marked line?

Yes! Change the line to:

   (push 5 v)

Zach
From: Josip Gracin
Subject: Re: Changing value of a list variable passed as argument to function
Date: 
Message-ID: <e5mhvj$9v0$1@sunce.iskon.hr>
Zach Beane wrote:
> A function can't do what PUSH does; PUSH manipulates a binding, while
> functions only have values to work with. (How would it handle the list
> NIL?)

I see it now.  It would obviously have to be a macro.

>> (defun doit ()
>>    (let ((v (list 1 2 3)))
>>      (let ((x v))
>>        (push 5 x)    ; <-- here
>>      v)))
>>
> Yes! Change the line to:
> 
>    (push 5 v)

Oops, I guess my isolated example was not really equivalent to what the 
original problem was. :-)

Anyway, I think you've given me all the answers I needed.  Thanks!
From: R. Mattes
Subject: Re: Changing value of a list variable passed as argument to function
Date: 
Message-ID: <pan.2006.06.01.11.39.18.433450@mh-freiburg.de>
On Thu, 01 Jun 2006 12:18:04 +0200, Josip Gracin wrote:

> Hello!
> 
> What's the proper way to pass a list variable to a function and have 
> that variable changed in a way 'push' macro would change it?  For 
> example, take a look at this code which isolates the problem.

To me it doesn't. There's no list passed to a function, afaict.

> (defun doit ()
>    (let ((v (list 1 2 3)))
>      (let ((x v))
>        (push 5 x)    ; <-- here
>      v)))
> 
> Function doit returns (1 2 3) and I believe I do understand why 
> (symbols, objects, places etc.). 

Do you? Where in you example is 'v' ever modified? 
>  Is there a simple way to make this 
> code return (5 1 2 3) by only changing the marked line?

???

(push 5 v)

Do am macorexpand on (push 5 x) and you'll understand ...


HTH Ralf Mattes
From: Vladimir Zolotykh
Subject: Re: Changing value of a list variable passed as argument to function
Date: 
Message-ID: <20060601140333.2ef39346.gsmith@eurocom.od.ua>
On Thu, 01 Jun 2006 12:18:04 +0200
Josip Gracin <······@tel.fer.hr> wrote:

> ...  Is there a simple way to make this 
> code return (5 1 2 3) by only changing the marked line?

AFAIK there isn't. X and V are like pointers here. The only
way to change the pointer is to change it itself. It is possible
to insert something in the V, thereby changing the V itself, but
not in the beginning.


-- 
Vladimir Zolotykh
From: Thomas A. Russ
Subject: Re: Changing value of a list variable passed as argument to function
Date: 
Message-ID: <ymislmog8yd.fsf@sevak.isi.edu>
Vladimir Zolotykh <······@eurocom.od.ua> writes:

> On Thu, 01 Jun 2006 12:18:04 +0200
> Josip Gracin <······@tel.fer.hr> wrote:
> 
> > ...  Is there a simple way to make this 
> > code return (5 1 2 3) by only changing the marked line?
> 
> AFAIK there isn't. X and V are like pointers here.

The best answer.  You shouldn't be doing this.  The standard Lisp
convention (or pattern, if you will) is to return the modified value and
have the function caller take care of changing the binding.

Failing to do this is one of the newby pitfalls in the use of
destructive functions like DELETE.

> The only
> way to change the pointer is to change it itself. It is possible
> to insert something in the V, thereby changing the V itself, but
> not in the beginning.

Well, actually, you can add destructively add something to the beginning
of a list.  It's just a little bit trickier to accomplish.  What you
can't do is destructively modify the empty list NIL.

(let ((v (list 1 2 3)))
  (setf (cdr v) (copy-list v))
  (setf (car v) 5)
  v)


-- 
Thomas A. Russ,  USC/Information Sciences Institute
From: Vladimir Zolotykh
Subject: Re: Changing value of a list variable passed as argument to function
Date: 
Message-ID: <20060602094739.617c14b9.gsmith@eurocom.od.ua>
On 01 Jun 2006 09:02:50 -0700
···@sevak.isi.edu (Thomas A. Russ) wrote:

> Vladimir Zolotykh <······@eurocom.od.ua> writes:
> 
> > On Thu, 01 Jun 2006 12:18:04 +0200
> > Josip Gracin <······@tel.fer.hr> wrote:
> > 
> > > ...  Is there a simple way to make this 
> > > code return (5 1 2 3) by only changing the marked line?
> > 
> > AFAIK there isn't. X and V are like pointers here.
> 
> The best answer.  You shouldn't be doing this.  The standard Lisp
> convention (or pattern, if you will) is to return the modified value and
> have the function caller take care of changing the binding.

My answer was to the OP _quoted_ question. Not about functions
returning modified values. I'm aware of their existance, however 
doubt that we should resort to them casually but for efficency
reasons.

-- 
Vladimir Zolotykh
From: Pascal Bourguignon
Subject: Re: Changing value of a list variable passed as argument to function
Date: 
Message-ID: <87verlvw4s.fsf@thalassa.informatimago.com>
Josip Gracin <······@tel.fer.hr> writes:
> What's the proper way to pass a list variable to a function and have
> that variable changed in a way 'push' macro would change it?  For
> example, take a look at this code which isolates the problem.
>
> (defun doit ()
>   (let ((v (list 1 2 3)))
>     (let ((x v))
>       (push 5 x)    ; <-- here
>     v)))
>
> Function doit returns (1 2 3) and I believe I do understand why
> (symbols, objects, places etc.).  Is there a simple way to make this
> code return (5 1 2 3) by only changing the marked line?

There is no such proper way.

In lisp, the type LIST is a hack: (OR CONS NULL)
But NIL is an immutable literal constant, so when you pass a list
to a function you may have NIL and you cannot change it.


Remember that arguments are passed by value, and that most values are
references.  But _variables_ are not passed as argument. Only _values_.


So, to do what you want easily, you could define your own list mutable
data type:

(defstruct mylist head)

Since structures are always passed by reference and are mutable,
you'll be able to push on it in the function:

(defun f (v) (push 0 (mylist-head v)))

(let ((v (make-mylist :head (list 1 2 3))))
   (f v)
   v)

--> #S(MYLIST :HEAD (0 1 2 3))


You can also use arrays, symbols, cons cells, clos objects, etc
instead of a structure, but structures or clos objects make a better
user data type since it's clear that it's a list.  With arrays,
symbols or cons cells, they would be indistinguishable from arrays,
symbols or cons cells.



Something else. You may be tempted to use CONS, and to avoid empty
lists:

(defun mypush (object cons)
  (push (car cons) (cdr cons))
  (setf (car cons) object)
  cons)

(defun f (v) (mypush 0 v))

(let ((v  (list 1 2 3)))
   (f v)
   v)

But the problem with this solution is that we lose the structure
sharing property. Compare:


(setf *print-circle* t)


(let* ((v (list 1 2 3))
       (w v))
  (push 0 v)
  (list v w))

--> ((0 . #1=(1 2 3)) #1#)


(let* ((v (list 1 2 3))
       (w v))
  (mypush 0 v)
  (list v w))

--> (#1=(0 1 2 3) #1#)


Again, the problem being that there's no LIST type in lisp. Only CONS
and NIL.  And when you push onto a list, you lose the list object
identity, but not when you modify a cons cell.  We take advantage of
this when we share list structure, which we couldn't do if we had a
true list type with true list objects.

-- 
__Pascal Bourguignon__                     http://www.informatimago.com/

This universe shipped by weight, not volume.  Some expansion may have
occurred during shipment.
From: Josip Gracin
Subject: Re: Changing value of a list variable passed as argument to function
Date: 
Message-ID: <e5nbad$ek7$1@sunce.iskon.hr>
Thanks to everybody who responded!

I suppose my attempt to simplify my real problem with the piece of code 
I've given was a mistake.  My real problem was the following.

I was playing with a basic loop for an event driven simulator.  There 
were functions like

(schedule agenda event time)  ; schedules an event
(empty-agenda-p agenda)
(initialize agenda) ; adds some initial events to the agenda
(pop-event agenda)  ; gets the first event in the queue
...

Now, my main loop was supposed to be something like:

(defun start-simulation ()
   (let ((agenda (list)))
     (initialize agenda)
     (loop
        (when ((empty-agenda-p agenda)) (return))
        (let ((event (pop-event agenda)))
           ...process event... etc...

And I've hit the problem where functions like pop-event and schedule 
cannot modify the agenda.

Anyway, I see now why that doesn't work.  Agenda, as defined, is a 
variable which contains a reference to the "object".  When passed as 
argument to a function, a new variable is created which references the 
same "object" as agenda (unless it's nil).

I've solved this by making agenda a CLOS object (containing a list of 
events) and defining methods on it.

I suppose another way, and I think the wrong way, to deal with this 
situation would be to use macros for all operations that need to modify 
agenda.

Thanks again for helpful answers!