Does an assignment result in a copy?

Started by lwix, November 02, 2004, 02:22:37 PM

Previous topic - Next topic

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
small\'s beautiful

Lutz

#1
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

#2
... 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

#3
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
(set! temp (f2 (f1 somelist)))I would like to do(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
small\'s beautiful

lwix

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



Lucas
small\'s beautiful

Lutz

#5
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

#6
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.
small\'s beautiful

lwix

#7
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
small\'s beautiful