Does an assignment result in a copy?

For the Compleat Fan
Locked
lwix
Posts: 24
Joined: Tue Oct 26, 2004 3:14 am
Location: New Zealand

Does an assignment result in a copy?

Post by lwix »

Hello,

in a large function, to decrease the number of nested function calls, I may wish to temporarily set the list (I'm working on) to a symbol. And then call the functions using the symbol as a reference and always resetting the sybol.

For e.g.
Instead of
(doz (doy (dox somelist)))
I could go
(begin
(set 'L (dox somelist)) ##
(set 'L (doy L))
(doz L))

My question: In the line marked ## above, does 'L get the original result of (dox somelist) [Assume the result is a list] or is another copy made of the resulting list for 'L and the original thrown away.

Thanks for your attention.

Lucas

Lutz
Posts: 5289
Joined: Thu Sep 26, 2002 4:45 pm
Location: Pasadena, California
Contact:

Post by Lutz »

Another copy is made. No two symbols in newLISP will ever point to the same piece of memory. If a function returns a list it is always a copy. If you want modify/pass always the same piece of memory pass a symbol. Symbols in are like '* pointers'.

There are many built in (destructive) functions changing the original list, i.e.:

(set 'lst '(a b c))
(push 'z lst)

lst => (z a b c)

But if you do:

(define (myPush e l) (push e l))

then:

(myPush 'z lst)

lst => (a b c)

will not change lst! To write a function to change the original list you would have to pass the symbol holding the list in a macro:

(define-macro (myPush e l) (push (eval e) (eval l)))

(myPush 'z lst)

lst => (z a b c)

BUT! most of the time just return the new list and assign it to the old symbol:

(define (myPush e l) (push e l) l)

(set 'lst (myPush 'a lst))

I know this goes against the grain of many old lispers, who expect lists beeing passed by reference, but in practice this is quite efficient in newLISP most of the time and if not then 'write that macro'.

Lutz
Posts: 5289
Joined: Thu Sep 26, 2002 4:45 pm
Location: Pasadena, California
Contact:

Post by Lutz »

... just think 'functional', like the inventors of LISP wanted it to be in the first place!

A few functions in newLISP change the original data object, they are listed in the manual chapter "Destructive versus non-destructive functions" , but most of the time you do just fine thinking in a pure 'functional' paradigm and not worry too much about efficiency. newLISP's memory management has been designed with the functional idea in mind.

Lutz

lwix
Posts: 24
Joined: Tue Oct 26, 2004 3:14 am
Location: New Zealand

Post by lwix »

Thank you Lutz for the taking the time.

I mostly understand newlisp's memory management and prefer it to oldlisp's. :-)

I understand that nice new fresh lists are returned from functions.

My question was not clear enough. It was a silly question (I hope).
Please allow me to try again.

Sometimes when there are too many nested functions I get confused so I break them up where it is convenient. The following example is contrived (and too small to need breaking) but it illustrates what I would sometimes like to do.

Instead of

Code: Select all

(set! temp (f2 (f1 somelist)))
I would like to do

Code: Select all

(begin
	(set! temp (f1 somelist))
	(set! temp (f2 temp))
)
I hope that setting 'temp in this way simply "sets" 'temp to the new returned list like we set a pointer to an address in C rather than the returned list being copied before 'temp is set to it (like when we assign structures in C -- a copy is made)

So is my understanding correct? Or do I pay a performance penalty when I "structure" code as above?

Sorry for the lack of clarity.

Many thanks for your patience.

Lucas

lwix
Posts: 24
Joined: Tue Oct 26, 2004 3:14 am
Location: New Zealand

Post by lwix »

I checked with (sys-info) and my assumption seems to be correct. But I'll wait for Lutz to confirm :-)

Lucas

Lutz
Posts: 5289
Joined: Thu Sep 26, 2002 4:45 pm
Location: Pasadena, California
Contact:

Post by Lutz »

In both cases you end up with 2 lists and the same memory usage, but more copying takes place, when you break up the function. 'set' is optimized, but there is also the task of getting the list out of temp in the broken up version. In my measurments it takes about 30% longer for the broken up solution.

I guess its always a tradeoff between readablility and speed.

Lutz

lwix
Posts: 24
Joined: Tue Oct 26, 2004 3:14 am
Location: New Zealand

Post by lwix »

Hmm, then I was right to ask the question.

Conclusion: no problem! I will just use meaningful function names and indentation!

Thank you for your attention.

Lucas.

lwix
Posts: 24
Joined: Tue Oct 26, 2004 3:14 am
Location: New Zealand

Post by lwix »

Another way to call-by-"reference" (macros are probably the better way to do it):

> ;;call-by-"reference" without using macros

> ;;example list

> (set 'a '(1 2 3))
(1 2 3)
> ;;set "pointer" to 'a

> (set 'p 'a)
a
> ;;"dereference" pointer

> (eval p)
(1 2 3)
> ;;define function to demonstrate call-by-"reference"

> (define (ref-pop _X) (pop (eval _X)))
(lambda (_X) (pop (eval _X)))
> ;;give it a go

> (ref-pop p)
1
> ;;check that we worked on the same list

> a
(2 3)
> (eval p)
(2 3)
> ;;newlisp is cool!

Regards
Lucas

Locked