passing objects by reference or value

Started by cormullion, December 16, 2006, 08:58:49 AM

Previous topic - Next topic

cormullion

Since it's quiet, can someone help me understand the difference between 'pass by value' and 'pass by reference'. I've looked at the manual, and I get the basic idea, but can't apply it easily yet in other situations...



Take this:


(set 'l:l (sequence 0 1000))

(define (reverse-list lst)
 (sort lst <)
 (nth-set 0 lst "first"))

(reverse-list l:l)


is this a pass by reference or a pass by value? What are the benefits of doing this? What happens if I miss out the colon..?

Lutz

#1
You are passing the list by value. After the function is finished l:l is still the same.



To pass by reference in newLISP you have to pass the context alone:


(set 'l:l (sequence 1 10))

(define (reverse-list lst)  
 (sort lst:l >)
 (nth-set 0 lst:l "first"))

(reverse-list l) ; passed by reference

l:l => ("first" 9 8 7 6 5 4 3 2 1)


Note that I am passing 'l' not 'l:l'.



Also the default functor is only recognized as such when it is located in the 'functor' position, which is right after the opening parenthesis, and  when doing implicit indexing for rest/slice.



You can do that for 'set-nth' but not for 'sort':


(define (reverse-list lst)  
 (sort lst:l >)
 (nth-set (lst 0) "first"))

(reverse-list l) ; passed by reference


... will still work.



Lutz

cormullion

#2
Hi Lutz. Thanks - one question, though:


(sort lst:l >)

here you've hard-coded the name of the list in the context? How would you write reverse-list so that it sorted the list passed in by the context?

Lutz

#3
Perhaps in that case a macro is handier:


(define-macro (reverse-list)
    (sort (eval (args 0)) >) (nth-set 0 (eval (args 0)) "first"))

(set 'l (sequence 1 10))

(reverse-list l)

l => ("first" 9 8 7 6 5 4 3 2 1)


Lutz

cormullion

#4
So - if I understand you - to pass by reference, you have to store your data in a context in a symbol with the same name as the context, then either pass the context name to the functions that can take implicit indexing of lists, or write a macro that lets you evaluate the context name and get the default symbol back before handing it to a function?



It doesn't seem right when I write it like this... Is there not an easier way to get the value of a default context symbol/functor...?

Lutz

#5
Quoteto pass by reference, you have to store your data in a context in a symbol with the same name as the context ...


No, it does not have to be the the same name as the context. The default funtor is only important when when you have a syntax like this: (<functor> x y z) in implicit indexing, or (<number> [<number>]<functor>) in implicit rest/slice when <functor> can be the context name alone.



This example in the manual shows both cases passing a context, with and without the default functor:



file:///usr/share/newlisp/doc/newlisp_manual.html#pass_big


QuoteIs there not an easier way to get the value of a default context symbol/functor...?


... the value yes, but not the reference.



Lutz

cormullion

#6
Is this passing by reference?


(set 'ctx:data (sequence 0 10000))

(define (reverse-list ctxt symbl)
  (sort (context ctxt symbl) >)
   (nth-set ((context ctxt symbl) 0) (rand 10)))

(define (p-ctxt ctxt)
 ; just a utility function
  (dotree (i ctxt)
    (println (eval i))))

(p-ctxt ctx)

(reverse-list ctx "data") ; passing 'ctx:data' by reference?


Is this going to be quicker than doing it the pass by value way?



(Thanks for the help Lutz btw)

Lutz

#7
Yes, this is passing the list by reference and you will see the speed difference on that list of 10,000 items; on smaller lists of lets say a 100 items the difference would not be much.



Lutz



ps: there will be a more intuitive way of reference passing in the future.

cormullion

#8
Thanks - I think I see the idea now. I didn't spot much of a speed benefit, because I rarely used lists large enough... to slow newLISP down...

Lutz

#9
Quote didn't spot much of a speed benefit, because I rarely used lists large enough... to slow newLISP down...


... that is exactly the point. Also the the same design decisions in newLISP which make it a mostly value-passing language are the design features which also make it small and fast. Big list objects where speed will be affected are rare enough to handle them as a global variable or take the extra step of putting a namespace/context wrapper around it.



The implicit indexing mechanism, in which default functors are permitted as list specifiers passing only the context, make this an easy affair for all functions taking indexed lists, i.e. (set-nth (db idx) value). In this case  the context name 'db' will default to the 'db:db' variable name which contains the list.



Where you ran into difficulties was with functions which take the whole list as an argument, like 'sort' in your examples. In that case you have to fully spell out the list identifier, i.e. (sort db:data) or (sort db:db) in case of the default functor. The expression (sort db) alone would not be permitted and returns an error. Your last example is a way around this limitation and some changes I am working on will make this even easier and more intuitive.



Lutz

cormullion

#10
Good to hear that more intuitive methods are on the way! So much better than when things get harder and more complicated with every release. My favourite situation is when there are hardly any exceptions to simple rules - life is much easier then, somehow.

cormullion

#11
Hi lutz - could you explain your new stuff a bit for me? In 9.0.11 - you have this:


(set 'foo:foo '(a b c d e f g))

(define (mysort ctx func)
   (sort (eval (ctx)) func))

(mysort foo)


Why do you need the eval there? 'when evaluating a context by itself it returns the default symbol'. So (ctx) should then return the default symbol to sort...  After all, this would work in the mysort function:


(sort c:foo func)

Lutz

#12
'sort' takes a list not the symbol. The following analogy between a normal symbol and the default symbol/functor explains it:


; example with normal symbol
(set 'lst '(4 3 6 5 4 3 7))
(sort lst) => (3 3 4 4 5 6 7)
(set 'ctx 'lst) => assigns the symbol

(symbol? ctx) => true
(sort ctx) => list expected in function sort : 'lst
(sort (eval ctx)) => (3 3 4 4 5 6 7)

; example with default symbol
(set 'foo:foo  '(4 3 6 5 4 3 7))
(sort foo:foo (3 3 4 4 5 6 7))
(set 'ctx foo) => assigns the context

(symbol? (ctx)) =>true
(sort (ctx)) => list expected in function sort : (ctx)
(sort (eval (ctx))) => (a b c d e f g)


Lutz

Lutz

#13
... the advantage of returning the symbol is that we can do things like this:


(set 'foo:foo 123)

(set 'ctx foo)

(set (ctx) 999)

foo:foo => 999


so when we pass a context as a parameter and it gets assigned to a parameter variable 'ctx', we can access 3 things: the context, the default functor of the context and the contents of the default functor. And the contents of a default functor can be anything: a list, a number, a string a function or whatever.



Lutz

cormullion

#14
ok - thanks. I think there's a hole in my understanding, i'll have to do some revision. :-)