Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Question about local variables in function

6 views
Skip to first unread message

Christian Soltenborn

unread,
Feb 12, 2004, 6:43:22 PM2/12/04
to
Hi,

I learned in my class that parameters of a function are call-by-value in
lisp (or messed something up badly, I'm an exchange student :-)

Now I have the following problem: if I call the following function with
a state as parameter, the output is fine, but the state itself changed, too.

(defun left (state)
(let ((pos (element-index state '0))
(cond
((equal '0 pos) nil)
((equal '3 pos) nil)
((equal '6 pos) nil)
(t (swap state (- pos 1) pos))
)
)
)

It's even the same if I modify the function like this:

(defun left (state)
(let ((pos (element-index state '0))
(tmpstate state))
(cond
((equal '0 pos) nil)
((equal '3 pos) nil)
((equal '6 pos) nil)
(t (swap tmpstate (- pos 1) pos))
)
)
)

The function swap looks like this:

(defun swap (list pos1 pos2)
(rotatef (elt list pos1) (elt list pos2))
list
)

Can anybody give me a hint what's going on?

Thanks in advance,
Christian

Brian Downing

unread,
Feb 12, 2004, 7:02:44 PM2/12/04
to
In article <c0h331$17ao9j$1...@ID-36219.news.uni-berlin.de>,

Christian Soltenborn <no_spam_c...@web.de> wrote:
> Now I have the following problem: if I call the following function with
> a state as parameter, the output is fine, but the state itself changed, too.

Find a Lisp reference that talks about list structure, understand it,
and you'll see why simply binding a new name to the list will not
prevent destructive effects.

(Hint - you're only holding on to the first cell of the list!)

> (defun left (state)
> (let ((pos (element-index state '0))
> (tmpstate state))
> (cond
> ((equal '0 pos) nil)
> ((equal '3 pos) nil)
> ((equal '6 pos) nil)
> (t (swap tmpstate (- pos 1) pos))
> )
> )
> )

Also, please try to learn and use standard Lisp indention style. Other
people reading your code will have an easier time, and eventually you
will as well. C-style indentation and C-style closing braces on their
own line is very bad for Lisp. Your editor should know what this is and
do it for you.

Compare:

(defun left (state)
(let ((pos (element-index state '0))
(tmpstate state))
(cond ((equal '0 pos) nil)
((equal '3 pos) nil)
((equal '6 pos) nil)
(t (swap tmpstate (- pos 1) pos)))))

Much more concise and readable.

-bcd
--
*** Brian Downing <bdowning at lavos dot net>

Edi Weitz

unread,
Feb 12, 2004, 7:13:35 PM2/12/04
to
On Thu, 12 Feb 2004 17:43:22 -0600, Christian Soltenborn <no_spam_c...@web.de> wrote:

> I learned in my class that parameters of a function are
> call-by-value in lisp

Read this thread (all of it):

<http://www.google.com/groups?threadm=m3n1f3mgal.fsf%40mindspring.com>

Edi.

Joe Marshall

unread,
Feb 12, 2004, 7:15:57 PM2/12/04
to
Christian Soltenborn <no_spam_c...@web.de> writes:

> Hi,
>
> I learned in my class that parameters of a function are call-by-value
> in lisp (or messed something up badly, I'm an exchange student :-)

They are. Call by value means exactly this:
If I define a function

(defun foo ()
(let ((x 33))
(bar x)
(print x)))

There is *no way* that you can write the function BAR so that the
PRINT expression prints something other than 33.

> Now I have the following problem: if I call the following function
> with a state as parameter, the output is fine, but the state itself
> changed, too.

You are passing an aggregate value and modifying the contents.

--
~jrm

Jeremy Yallop

unread,
Feb 12, 2004, 7:24:04 PM2/12/04
to
Joe Marshall wrote:
> Call by value means exactly this:
> If I define a function
>
> (defun foo ()
> (let ((x 33))
> (bar x)
> (print x)))
>
> There is *no way* that you can write the function BAR so that the
> PRINT expression prints something other than 33.

(defun bar (x)
(setf *print-base* 34))

Joe Marshall

unread,
Feb 12, 2004, 7:35:26 PM2/12/04
to
Jeremy Yallop <jer...@jdyallop.freeserve.co.uk> writes:

I spoke too soon. (there is always a wiseass)

(defun foo ()
(let ((x 33))
(bar x)

(= x 33)))

There is *no way* that you can write the *function* BAR (assuming
normal bindings of DEFUN LET = and no funny stuff with X) such that
the return value of this function (assuming it returns normally) is
other than 't.

--
~jrm

Pascal Bourguignon

unread,
Feb 13, 2004, 2:07:39 AM2/13/04
to
Joe Marshall <prunes...@comcast.net> writes:

Yes, but that does not change the puzzling of beginners who can see that:

(defun foo ()
(let ((x (list 3 3)))
(bar x)
(equal x (list 3 3))))

can return t or nil depending on what's in bar:

(defun bar (x)
(incf (car x))
(decf (cadr x)))

Here too, x is passed by value. Only the value that is passed is the
first cons (actually, a reference to the first cons, conses are not
copied gratuituously) of the list built in the let, and a cons
contains two references.


In C too, all the parameters are passed by value. That does not
prevent the programmer to pass references (pointers):

void getvalue(int index,int* value)
{
*value=42;
/* ^__ special syntax */
}

getvalue(1,&value);
/* ^__ special syntax */


Contrast this with pascal where you have the choice between passing by
value or by reference, that is without needing any special syntax at
the call site or within the procedure:

procedure getvalue(index:integer;var value:integer);
begin
value=42;
end;

getvalue(1,value);

In lisp you can box values into a cons or an array or a structure or
some other compound value:

(defun getvalue (index valref)
(setf (car valref) 42))

(let ((value (cons 3 nil)))
(getvalue 1 value)
(assert (= (car value) 42)))

Or, said otherwise, if you have a cons, or an array or structure or
some other compound value, you can change it in spite of the
call-by-value aspect. This is called a destructive operation, or a
side-effect. Also, care should be taken, because such a compound value
may be the source of the lisp program!


[12]> (defun getvalue (index valref)
(setf (car valref) 42))
GETVALUE
[18]> (function-lambda-expression 'foo)
(LAMBDA NIL (DECLARE (SYSTEM::IN-DEFUN FOO))
(BLOCK FOO
(DOTIMES (I 3)
(LET ((X '(3 4 5)))
(IF (= (CAR X) 3) (GETVALUE 1 X) (FORMAT T "oops!~%")))))) ;
#(NIL NIL NIL NIL ((DECLARATION VALUES OPTIMIZE DECLARATION))) ;
FOO
[19]> (foo)
oops!
oops!
NIL
[20]> (function-lambda-expression 'foo)
(LAMBDA NIL (DECLARE (SYSTEM::IN-DEFUN FOO))
(BLOCK FOO
(DOTIMES (I 3)
(LET ((X '(42 4 5))) ;;<<<==== OH! OH!
(IF (= (CAR X) 3) (GETVALUE 1 X) (FORMAT T "oops!~%")))))) ;
#(NIL NIL NIL NIL ((DECLARATION VALUES OPTIMIZE DECLARATION))) ;
FOO
[21]>

--
__Pascal_Bourguignon__ http://www.informatimago.com/
There is no worse tyranny than to force a man to pay for what he doesn't
want merely because you think it would be good for him.--Robert Heinlein
http://www.theadvocates.org/

Lars Brinkhoff

unread,
Feb 13, 2004, 2:24:21 AM2/13/04
to
Brian Downing <see-si...@lavos.net> writes:

> Christian Soltenborn <no_spam_c...@web.de> wrote:
> > (defun left (state)
> > (let ((pos (element-index state '0))
> > (tmpstate state))
> > (cond
> > ((equal '0 pos) nil)
> > ((equal '3 pos) nil)
> > ((equal '6 pos) nil)
> > (t (swap tmpstate (- pos 1) pos))
> > )
> > )
> > )
> Also, please try to learn and use standard Lisp indention style.

Also, there's no need to quote numbers, i.e. you can write 3 instead
of '3 etc.

--
Lars Brinkhoff, Services for Unix, Linux, GCC, HTTP
Brinkhoff Consulting http://www.brinkhoff.se/

Coby Beck

unread,
Feb 13, 2004, 4:43:26 AM2/13/04
to

"Jeremy Yallop" <jer...@jdyallop.freeserve.co.uk> wrote in message
news:slrnc2o694...@hehe.cl.cam.ac.uk...

> Joe Marshall wrote:
> > Call by value means exactly this:
> > If I define a function
> >
> > (defun foo ()
> > (let ((x 33))
> > (bar x)
> > (print x)))
> >
> > There is *no way* that you can write the function BAR so that the
> > PRINT expression prints something other than 33.

You are of course correct. But:
> (defun foo ()
(let ((x (list 33)))
(bar x)
x))
FOO

> (defun bar (a)
(incf (car a)))
BAR

> (foo)
(34)

Not very helpful...

--
Coby Beck
(remove #\Space "coby 101 @ big pond . com")


Nils Gösche

unread,
Feb 13, 2004, 8:39:54 AM2/13/04
to
"Coby Beck" <cb...@mercury.bc.ca> writes:

If it's not helpful, why not explain it?

The point is:

(defun foo ()
(let* ((x <whatever>) ; x is bound to some object
(y x)) ; y is bound to the same object
(bar x) ; this object is passed to BAR
(eql x y))) ; is x still bound to the same object as before?

will always return T (assuming BAR is a function and not a macro and
neither of X and Y are special variables or symbol macros or whatnot
and that DEFUN and LET and EQL have their normal meaning, and nobody
screwed up the readtable or the printer or the package system or is
using Perl and not Lisp or whatever else somebody might do to break
this. Can we skip that kind of stuff for the moment? And please
don't confuse newbies with EQ, either; thanks.).

Regards,
--
Nils Gösche
"Don't ask for whom the <CTRL-G> tolls."

PGP key ID 0x0655CFA0

Wolfhard Buß

unread,
Feb 13, 2004, 1:05:07 PM2/13/04
to
* Christian Soltenborn writes:

> (defun swap (list pos1 pos2)
> (rotatef (elt list pos1) (elt list pos2))
> list)
>
> Can anybody give me a hint what's going on?

Pauls non-consing swap is dangerous! It affects data, accessible
through its arguments, as a side effect. Call it nswap, and use it
with caution.

A non-destructive swap

(defmethod swap ((list list) (pos1 integer) (pos2 integer))
(let* ((cut (1+ (max pos1 pos2)))
(prefix (copy-seq (subseq list 0 cut))))
(rotatef (elt prefix pos1) (elt prefix pos2))
(concatenate 'list prefix (subseq list cut))))

for your left function.

(defun left (state)
(let ((pos (element-index state 0)))
(case pos
((0 3 6) nil)
(t (swap state (1- pos) pos)))))

Hope this helps.

--
"Hurry if you still want to see something. Everything is vanishing."
-- Paul Cézanne (1839-1906)

Joe Marshall

unread,
Feb 13, 2004, 6:20:17 PM2/13/04
to

>> Joe Marshall wrote:
>> > Call by value means exactly this:
>> > If I define a function
>> >
>> > (defun foo ()
>> > (let ((x 33))
>> > (bar x)
>> > (print x)))
>> >
>> > There is *no way* that you can write the function BAR so that the
>> > PRINT expression prints something other than 33.
>

"Coby Beck" <cb...@mercury.bc.ca> writes:
> You are of course correct. But:
>> (defun foo ()
> (let ((x (list 33)))
> (bar x)
> x))
> FOO
>
>> (defun bar (a)
> (incf (car a)))
> BAR
>
>> (foo)
> (34)
>
> Not very helpful...

That's sort of the intent! Not that I don't want to be helpful, but I
want to illustrate that call-by-value is about the *variable*, not
about what it holds.

--
~jrm

Wolfhard Buß

unread,
Feb 14, 2004, 3:26:01 AM2/14/04
to
* I wrote:

> A non-destructive swap
>
> (defmethod swap ((list list) (pos1 integer) (pos2 integer))
> (let* ((cut (1+ (max pos1 pos2)))
> (prefix (copy-seq (subseq list 0 cut))))
> (rotatef (elt prefix pos1) (elt prefix pos2))
> (concatenate 'list prefix (subseq list cut))))

The attentive reader wonders whether swaps behavior for eql pos1 pos2
is a bug or a feature...

Coby Beck

unread,
Feb 14, 2004, 5:10:59 AM2/14/04
to

"Nils Gösche" <car...@cartan.de> wrote in message
news:lyn07nq...@cartan.de...

Sorry, I should have either done that or kept my mouth shut.

> The point is:
>
> (defun foo ()
> (let* ((x <whatever>) ; x is bound to some object
> (y x)) ; y is bound to the same object
> (bar x) ; this object is passed to BAR
> (eql x y))) ; is x still bound to the same object as before?

This is a better way to to put it! And now we get into the whole EQ EQL
EQUAL quagmire! (for newbies that is, I think it is in fact all just so
Right when you do get it)

0 new messages