Set or add a pair to a list by reference

Started by Tim Johnson, February 04, 2010, 06:03:51 PM

Previous topic - Next topic

Tim Johnson

Here's the sample code:
(println "<pre>")
(define (set-pair lst key val)  ;; set a pair if found or add it if not found
(unless(set-ref (list key '?) lst (list key val) match)
(push (list key val) attrs -1)))
(set 'attrs '(("ID" "form0") ("method" "POST") ("name" "form0") ("action" "[[action]]")   ;; sample target list
("onsubmit" "return checkLoginForm(this);")))
(println "Original List:")
(println attrs)
(set 'attr "action")  ;; existing key
(set 'value "mycgiscript.lsp")  ;; new value
(set 'attrs(set-pair attrs attr value))  ;; call the function
(println "nList Pair Modified:")
(println attrs)   ;; test the result
(set 'attr "newaction")  ;; non-existing (new) key
(set 'value "make weave not lore")  ;; value for key
(set 'attrs(set-pair attrs attr value))  ;; call it
(println "nList Pair Added:")           ;; display results
(println attrs)


Now, here are the results:
Original List:
(("ID" "form0") ("method" "POST") ("name" "form0") ("action" "[[action]]") ("onsubmit"
  "return checkLoginForm(this);"))

List Pair Modified:
(("ID" "form0") ("method" "POST") ("name" "form0") ("action" "mycgiscript.lsp") (
  "onsubmit" "return checkLoginForm(this);"))

List Pair Added:
(("ID" "form0") ("method" "POST") ("name" "form0") ("action" "mycgiscript.lsp") (
  "onsubmit" "return checkLoginForm(this);")
 ("newaction" "make weave not lore"))

And it works just like I want it to, but three questions evolve from this exercise:

1)How can I pass the 'lst argument by reference so that the 'set-pair function becomes destructive?

Example: (set-pair attrs attr value) ;; => 'attrs is changed with calling 'set
2)I come from a background (most recently) in python and rebol, both which pass variables

by reference.

3)I don't believe that Lutz does anything for no good reason:

Why do  most newlisp functions pass arguments by value?

Thanks

tim
Programmer since 1987. Unix environment.

Kazimir Majorinc

#1
1)How can I pass the 'lst argument by reference so that the 'set-pair function becomes destructive?



If that is what you want, write your functions so they accept references, not arguments by references. You can find few examples here]2) I come from a background (most recently) in python and rebol, both which pass variables by reference.[/b]



Hm... you skipped the question.



3) I don't believe that Lutz does anything for no good reason: Why do most newlisp functions pass arguments by value?



I think that the main reason for that is - ORO. Each object in memory is referenced by only one symbol. If you have function (define (f x) ... ) and it accepts arguments by reference, and you call it with (f y), then y and x both point on the same value. The trick is: think about Newlisp symbols not as variables, but as (generalized) memory addresses in other languages. Just like in other languages, one object in memory cannot be stored in two memory adresses, on the same way, it cannot be stored in two symbols in Newlisp.



Because of that, Newlisp frequently requires one indirection more than other languages (just like assembler does.) But Newlisp is powerful enough so we do not notice it at the first moment, but only when we need to pass large objects as arguments - and our experience with other languages (including other Lisps) might be misleading.  And why ORO? There are two other memory management model - manual and garbage collection. Lutz has find some middle ground that allow programmer to do some things without manual memory allocation and deallocation, which is hard on programmer - and without garbage collection - which has its own contra's.



But - for those who really want passing by reference - they can redefine whole Newlisp to do something like:



(set-indirect  'x <expression>) <==> (begin (set 'x 'x-indirection) (set 'x-indirection <expression>))



and then redefine *all* functions in Newlips to accept not only normal arguments, but also the symbols of the form x-indirect and make that indirection.



This is how it can be done:



(define (indirection? ix)
  (and (symbol? ix)
       (ends-with (string (eval ix)) "-indirection")))      

(dolist (function-name (filter (lambda(x)(primitive? (eval x))) (symbols)))
  (letex((new-function-name (sym (append (string function-name) "-indirect")))
         (function-name function-name))
      (println "Defining " 'new-function-name " using " 'function-name "... ")
     
      (define (new-function-name)
           (let ((indirected-vars (filter indirection? (args))))
           (eval (append (list 'expand (append '(function-name) (args))) (map quote indirected-vars)))))))
           
(define (set-indirect s1 expr1)
   (let ((s1-indirection-name (sym (append (string s1) "-indirection"))))
        (set s1 s1-indirection-name)
        (set s1-indirection-name expr1)))
       
;-----------

(set-indirect 'L '(1 2 3 4 5))

(define (my-reverse-and-insert-zeroes z)
        (reverse-indirect z)
        (push-indirect 0 z)
        (push-indirect 0 z -1))

(my-reverse-and-insert-zeroes L)
(println-indirect "And finally ... " L) ; And finally ... (0 5 4 3 2 1 0)

(set 'M L)
(my-reverse-and-insert-zeroes M)
(println-indirect "Where is ORO? " L M) ;Where is ORO? (0 0 1 2 3 4 5 0 0)(0 0 1 2 3 4 5 0 0)

(exit)


Don't take this code seriously, it is 15 lines written in some half of the hour. It is only proof of the concept, i.e. how Newlisp can be developed in that direction. You can say that one goes through hoops only to achieve what other languages have out of the box, but that kind of flexibility is the essence of Lisp.[/color]
http://kazimirmajorinc.com/\">WWW site; http://kazimirmajorinc.blogspot.com\">blog.

Lutz

#2
If you want to pass by reference, you also can use the new FOOP (since development version 10.1.8) as defined here: http://www.newlisp.org/downloads/development/newLISP-10.2-Release.html">http://www.newlisp.org/downloads/develo ... lease.html">http://www.newlisp.org/downloads/development/newLISP-10.2-Release.html and stable in current development version 10.1.10.

Tim Johnson

#3
Kazimir:

The fix is really quite easy. The code becomes
(define (set-pair lst key val)
(unless(set-ref (list key '?) (eval lst) (list key val) match)
(push (list key val) (eval lst) -1)))

And the call becomes (set-pair 'attrs attr value)  ;; no 'set!
Note the use of (eval lst) in the function body and the quoting of the argument in the call.

But the peripheral discussion is of great value to me. Thank you for the code examples

and especially the explanation.

After having my coffee, I realize that question 2) was really not a question, but suffice it

to say, that coming from python, the associative structure of dictionary is quite handy, but

the b-tree nature of the internals makes ordering impossible with the native dict, although derivative

classes with ordering can (and are) created.



I think it would be great if 'set-pair were a native (written in C) newlisp function, but that is just me.

thanks again

tim (finishing-my-coffee 'mug)
Programmer since 1987. Unix environment.

Tim Johnson

#4
Quote from: "Lutz"If you want to pass by reference, you also can use the new FOOP (since development version 10.1.8) as defined here: http://www.newlisp.org/downloads/development/newLISP-10.2-Release.html">http://www.newlisp.org/downloads/develo ... lease.html">http://www.newlisp.org/downloads/development/newLISP-10.2-Release.html and stable in current development version 10.1.10.

Thank you Lutz. I will lay low for a while and try to to digest all of this.
Programmer since 1987. Unix environment.

itistoday

#5
Quote from: "Tim Johnson"
Quote from: "Lutz"If you want to pass by reference, you also can use the new FOOP (since development version 10.1.8) as defined here: http://www.newlisp.org/downloads/development/newLISP-10.2-Release.html">http://www.newlisp.org/downloads/develo ... lease.html">http://www.newlisp.org/downloads/development/newLISP-10.2-Release.html and stable in current development version 10.1.10.

Thank you Lutz. I will lay low for a while and try to to digest all of this.


Obligatory mention of Objective newLISP. It lets you do real pass-by-reference, whereas the new FOOP does not. FOOP (since 10.1.8) has been improved to let you modify a FOOP object without having to create a copy of it, but when you pass the objects around you're still copying them. Link to ObjNL in sig.
Get your Objective newLISP groove on.

Lutz

#6
Quotebut when you pass the objects around you're still copying them


If you stay in FOOP when passing on objets that is not true. See this example from qa-foop:


(new Class 'A)
(new Class 'B)

(setq a (A 0 (B 0)))

(define (B:m str)
    (inc (self 1)) )

(define (A:m str)
    (inc (self 1)))

(define (A:qa1)
    (:m (self) (:m (self 2) " hello"))
    (self))

(define (A:qa2)
    (:m (self 2) (:m (self) " hello"))
    (self))

(if (and
        (= (:qa1 a) '(A 1 (B 1)))
        (= (:qa2 a) '(A 2 (B 2))))
    (println ">>>>> FOOP TESTING SUCESSFUL")
    (println ">>>>> ERROR: IN FOOP TESTING")
)


The object gets passed on as 'self' from the ':qa' method to the ':m' method as a reference not as a copy.

itistoday

#7
Quote from: "Lutz"If you stay in FOOP when passing on objets that is not true. See this example from qa-foop:


Ah, I didn't know that, but what I said is still mostly true, in 90% of the cases where objects are passed around in a normal OO language, in FOOP they would be passed by value, not by reference.



Here's a trivial example:


> (new Class 'A)
A
> (new Class 'B)
B
> (define (A:mod b) (:setStr b "foo"))
(lambda (b) (: setStr b "foo"))
> (define (B:setStr str) (setf (self 1) str))
(lambda (str) (setf (self 1) str))
> (set 'a (A) 'b (B "bar"))
(B "bar")
> (:mod a b)
"foo"
> b
(B "bar")


So, you can't use normal functions to pass FOOP objects by reference, and you can't use FOOP methods to modify objects by reference when they are modifying a different object, not themselves.
Get your Objective newLISP groove on.

Lutz

#8
Quote from: "itistoday"So, you can't use normal functions to pass FOOP objects by reference

but that is, what I said already:


Quote from: "lutz"If you stay in FOOP when passing on objects that is not true.

You have to stay in FOOP. Your definition of A:mod is not staying in the FOOP paradigm.

itistoday

#9
Quote from: "Lutz"You have to stay in FOOP. Your definition of A:mod is not staying in the FOOP paradigm.


Fine, but all that means is that FOOP is a very narrow subset of OOP. Tim wanted to know how to do reference passing in newLISP and I'm just pointing out that most of the time, FOOP is not a good way of doing it.
Get your Objective newLISP groove on.

Tim Johnson

#10
What I would like to comment on is not meant to add to or detract from any of conversations going on in this thread,

but consider my observations to be parallel to the topic:



Oftentimes, it appears to me that the concept of OOP becomes a sort of benchmark with which one system or programming

language is measured against another. My experience has been is that OOP is a paradigm, and one of many. A slightly different

perspective was given to me by one of my nieces, also a programmer, who at one time worked for Carl Sassenrath, the developer

of rebol and before that - the amiga operating system. According to her, Carl was fond of saying:
QuoteData drives it!


I refer also to "The Pragmatic Programmer" by Hunt & Thomas and their chapter on Metaprogramming and Metadata. This is

is further impressed on me when I recall my days working with Microsoft Access and it's Property List Driven interfaces.



Python is very strictly engineered and very OOP in nature, yet, it has occurred to me as programmer who usually working on a Code Base alone,

(I often work on multi-programmer projects but individuals work on their own codebase) that perhaps a data-driven paradigm needs

as much of a look as the OOP approach. Indeed, both rebol and newlisp can introspect and manipulate symbols, can use data and code

interchangeably in a way that python does not do. This is why as I learn newlisp, I shall focus more on the functional and list processing

aspects.



And that is why - although I am very interested in newlisp's progress towards OOP/FOOP - I will begin by looking at the list processing and

the functional aspects. Whether passed by reference or not. :)
Programmer since 1987. Unix environment.

Tim Johnson

#11
Example follows. First the data structure that "drives it"
set form-data [
"txDBS"[type "select" txt[llabel "Muni:" lj 1] atr[data [(munis)] choice (muni) size 4]]
"apn" [type "text" txt[llabel "Enter Apn:" lj 1] atr[value (any[apn ""]) size 20 maxlength 20 req[(if not mu/get 'apns)]]
"apns"[type "select" txt[llabel "OR - Choose APN:" lj 1] atr[data [(*apns*)] size (apn-size)]]
"submit" [type "submit" txt[llabel ""] atr[value "Next =>"]]
"reset" [type "reset" txt[llabel ""] atr[value "Reset"]]
"task" [type "hidden" txt[] atr[value (cgi/get 'task)]]
"subtask" [type "hidden" txt[] atr[value "save"]]
]

To translate to newlisp think thusly: Insert 'eval after a opening paren. Convert opening braces to opening parens, closing braces to closing parens

We see that datastructure can contain code also, in the case of the 'req value for key "apn"

Now the 'mu "context" 'loads the data structure
mu/load form-data ;; the forward slash is 'something' like the colon in newlisp

Now we render the data structure:
print htm[
form/action/method (path) "POST"[
table[
tr[td/align "right"[(mu/pop-label "apn")]
  td/align "left"[(mu/render "apn")]]
tr[td/align "right"[(mu/pop-label "txDBS")]
  td/align "left"[(mu/render "txDBS")]]
(apn-list)
tr[td/align "right"[(mu/render "submit")]
  td/align "left"[(mu/render "reset")]]]
(mu/render "task")(mu/render "subtask")]]

So far, everything from rebol seems translate well to newlisp.

Data drives it.

Go Saints.
Programmer since 1987. Unix environment.