Page 1 of 1
how to return an association list
Posted: Fri Nov 03, 2006 2:42 pm
by cormullion
Is it possible to return an association list from a function? Here's a test:
Code: Select all
(define (test)
(local (a b c)
(set 'a 1)
(set 'b 2)
(set 'c 3)
(set 'sum (+ a b c))
'(("a" a) ("b" b) ("c" c) ("sum" sum))
))
(println (lookup "sum" (test)))
I think this is a newbie question... :-)
Posted: Fri Nov 03, 2006 3:03 pm
by Lutz
Yes, a small change does it:
Code: Select all
(define (test)
(letex (a 1 b 2 c 3)
(letex (sum (+ a b c))
'(("a" a) ("b" b) ("c" c) ("sum" sum)) )
) )
(test) -> (("a" 1) ("b" 2) ("c" 3) ("sum" 6))
Lutz
Posted: Fri Nov 03, 2006 4:13 pm
by cormullion
Thanks, Lutz. I think i get it - the symbols need expanding before their values are lost?
But I'd like to use the (local) construction, and avoid those two nested letexes which are too unintuitive for me to remember... :-) Can I use expand while building an assoc list?
Code: Select all
(define (test)
(local (a b c)
(set 'a 1)
(set 'b 2)
(set 'c 3)
(set 'sum (+ a b c))
(expand (list a b c))))
will return (1 2 3).
Posted: Fri Nov 03, 2006 4:58 pm
by Lutz
Like this?
Code: Select all
(define (test)
(letn (a 1 b 2 c 3 sum (+ a b c))
(expand '(("a" a) ("b" b) ("c" c) ("sum" sum)) 'a 'b 'c 'sum)))
(test) -> (("a" 1) ("b" 2) ("c" 3) ("sum" 6))
Lutz
Posted: Fri Nov 03, 2006 6:54 pm
by cormullion
yes, that looks better .... ;-)
thanks
PS quiet round here isn't it?
Posted: Fri Nov 03, 2006 9:45 pm
by Fanda
Lutz wrote:
Code: Select all
(define (test)
(letex (a 1 b 2 c 3)
(letex (sum (+ a b c))
'(("a" a) ("b" b) ("c" c) ("sum" sum)) )
) )
(test) -> (("a" 1) ("b" 2) ("c" 3) ("sum" 6))
This makes me think - maybe we could create a new 'letexn' :-)
Code: Select all
(define (test)
(letexn (a 1 b 2 c 3 sum (+ a b c))
'(("a" a) ("b" b) ("c" c) ("sum" sum)) ))
Fanda
Posted: Fri Nov 03, 2006 9:53 pm
by Fanda
Another solution:
Code: Select all
(define (test)
(letn (a 1 b 2 c 3 sum (+ a b c))
(list (list "a" a) (list "b" b) (list "c" c) (list "sum" sum))))
Fanda
Posted: Sat Nov 04, 2006 8:00 am
by cormullion
thanks Fanda. I think this is the one I shall use:
Code: Select all
(define (test)
(local (a b c sum)
(set 'a 1 'b 2 'c 3 'sum (+ a b c))
(list (list "a" a) (list "b" b) (list "c" c) (list "sum" sum))))
(println (lookup "sum" (test)))
It reads nicely... ;-)
Posted: Sat Nov 04, 2006 4:27 pm
by m i c h a e l
cormullion!
With the addition of the
pair function:
Code: Select all
(constant (global 'pair) (fn (lst)
(array-list (array (/ (length lst) 2) 2 lst))))
You can bum it even more:
Code: Select all
(define (test)
(local (a b c sum)
(set 'a 1 'b 2 'c 3 'sum (+ a b c))
(pair (list "a" a "b" b "c" c "sum" sum))))
pair. Never leave home without it ;-)
m i c h a e l
Posted: Sat Nov 04, 2006 5:10 pm
by cormullion
Welcome back, mm. :-) But what does pair do? - why is it using arrays?
Posted: Sat Nov 04, 2006 5:24 pm
by m i c h a e l
I've never really been away. Just went back to my more natural wallflower mode ;-)
pair was taken from the Code Snippets page on newLISP.org.
pair turns this: (1 2 3 4 5 6 7 8 9 10) into this: ((1 2) (3 4) (5 6) (7 8) (9 10)).
As to "why the arrays," you'll have to ask the original author about that one :-)
m i c h a e l
Posted: Sat Nov 04, 2006 6:25 pm
by cormullion
It looks like the Hand of Fanda...
Probably using arrays for speed...
It's one of those situations where you kind of want the functionality to be already there - you don't want to define other functions just to get your existing functions to work the way you want them to. But you have to draw the line somewhere. And besides, we're supposed to be bending the language to suit our application, so perhaps it's a good thing to do.
Posted: Sat Nov 04, 2006 7:02 pm
by Fanda
I am not sure, who made a 'pair' function, but lets guess... Lutz? ;-)
Be careful - pair is losing it's elements, if they are not even:
> (pair '(1 2 3 4))
((1 2) (3 4))
> (pair '(1 2 3))
((1 2))
I like group: (group lst [n] [keep-tail])
Code: Select all
(define (group lst (n 2) (keep-tail true))
(set 'lst (map (lambda (i) (slice lst i n)) (sequence 0 (- (length lst) 1) n)))
(if (and (not keep-tail) (!= n (length (last lst))))
(pop lst -1))
lst)
> (group '(1 2 3 4))
((1 2) (3 4))
> (group '(1 2 3))
((1 2) (3))
> (group '(1 2 3 4 5 6 7 8) 3)
((1 2 3) (4 5 6) (7 8))
> (group '(1 2 3 4 5 6 7 8) 3 nil)
((1 2 3) (4 5 6))
Fanda
Posted: Sat Nov 04, 2006 8:47 pm
by Lutz
The 'array' function was used because it has pairing/grouping already built into it, and it can take a flat list for initialization:
Code: Select all
(array 3 2) => ((nil nil) (nil nil) (nil nil))
; or with initialization
(array 3 2 '(1 2 3 4 5 6)) => ((1 2) (3 4) (5 6))
It also would work for more than 2 dimensions. Similar to Fanda's group function you could define:
Code: Select all
(define (group lst (n 2))
(array-list (array (/ (length lst) n) n lst)))
Since version 9.0 the functions 'append, last, first, rest and slice' (and their implicit indexing forms) can also be used on arrays. This means that very often it is not necessary to convert the array back into a list using 'array-list'.
For shorter lists (rule of thumb: < 100 elements) arrays don't offer much of a speed advantage, but on longer lists and when accessing elements not in a sequential fashion, the speed advantage can be dramatic.
Lutz
Posted: Sat Nov 04, 2006 9:52 pm
by cormullion
Thanks Lutz. The idea was that I thought it would be a useful way to return a set of results from a function, by returning them in an association list. Speed isn't much of an issue, since there would be only 5 to 10 values to return. Ease of expression is more the thing - something that naturally flows out of the fingers once the hard work of defining the function body is completed, that doesn't require another piece of program code to achieve. That's why I'm currently leaning towards the (list (list approach.
Posted: Sat Nov 04, 2006 11:01 pm
by Lutz
Ease of expression is more the thing ... that doesn't require another piece of program code to achieve.
I wholeheartedly agree. Ease of expression and self sufficiency should always be the first priority, before efficiency considerations, when coding. Decisions in this area always have a subjective component, which is fine and how it should be.
Because of this I also reject 'standardized coding styles' or the notion that there is a 'right way' of doing things.
Much of the fun in programming stems from the freedom we can take to express (code) in our own individual way and a prgrammming language should allow this.
The best programmers are those who take the freedom to cultivate their own styles.
Lutz
Posted: Sat Nov 04, 2006 11:50 pm
by lisp
Lutz wrote:Ease of expression is more the thing ... that doesn't require another piece of program code to achieve.
I wholeheartedly agree. Ease of expression and self sufficiency should always be the first priority, before efficiency considerations, when coding. Decisions in this area always have a subjective component, which is fine and how it should be.
Because of this I also reject 'standardized coding styles' or the notion that there is a 'right way' of doing things.
Much of the fun in programming stems from the freedom we can take to express (code) in our own individual way and a prgrammming language should allow this.
The best programmers are those who take the freedom to cultivate their own styles.
Lutz
Quote book material.
Posted: Sun Nov 05, 2006 4:41 am
by rickyboy
Yet another:
Code: Select all
(define (zip) (transpose (args)))
(define (test)
(local (a b c sum)
(set 'a 1)
(set 'b 2)
(set 'c 3)
(set 'sum (+ a b c))
(zip '(a b c sum) (list a b c sum))))
> (test)
((a 1) (b 2) (c 3) (sum 6))
To understand zip's meaning, think about what happens to two separate rows of teeth as they pass through a zipper.
Cheers, --Rick
Posted: Sun Nov 05, 2006 8:39 am
by cormullion
That's neat! And inline:
Code: Select all
(transpose (list '(a b c sum) (list a b c sum)))
works for me, and is shorter than:
Code: Select all
(list (list "a" a) (list "b" b) (list "c" c) (list "sum" sum))
thanks for all the ideas!
Posted: Sun Nov 05, 2006 9:31 am
by Lutz
Very nice, rickyboy ... and works for n-way zips too:
Code: Select all
(zip '(1 2 3) '(a b c) '(x y z)) => ((1 a x) (2 b y) (3 c z))
I will put this into the
http://newlisp.org/index.cgi?page=Code_Snippets page
Lutz
Posted: Mon Nov 06, 2006 12:12 am
by rickyboy
Oops! You had better not give me credit for that. The idea and name of 'zip' is something taken from FP languages like Haskell or ML. The newlisp implementation of 'zip' I first saw in Fanda's function 'merge' in
http://www.intricatevisions.com/source/newlisp/list.lsp. When I saw Fanda's 'merge', I thought "Hey that's zip and a very pithy definition at that." So, I vote that you strike my name from the Snippets credit and stick Fanda's name there. :-) Cheers, --Rick
Posted: Mon Nov 06, 2006 12:31 am
by nigelbrown
For an unspecified number of arguments:
(define-macro (test )
(let ((s (map (lambda (x) (list (sym x) (eval x))) (args))))
(append s (list (list 'sum (apply '+ (map (lambda (x) (x 1)) s)))))))
Line 2 gathers the arguments into an assoc list
Line 3 sums the second elements and sticks it on the end
the local variable s is used so arguments are only evaluated once
so
> (setq a 1)
1
> (setq b 2)
2
> (setq c 3)
3
> (test a b c)
((a 1) (b 2) (c 3) (sum 6))
>
Nigel
PS to keep the original intent of strings in the list use name:
(define-macro (test )
(let ((s (map (lambda (x) (list (name x) (eval x))) (args))))
(append s (list (list "sum" (apply '+ (map (lambda (x) (x 1)) s)))))))
gives
> (test a b c)
(("a" 1) ("b" 2) ("c" 3) ("sum" 6))
>
Posted: Mon Nov 06, 2006 2:28 am
by Fanda
rickyboy wrote:Oops! You had better not give me credit for that. The idea and name of 'zip' is something taken from FP languages like Haskell or ML. The newlisp implementation of 'zip' I first saw in Fanda's function 'merge' in
http://www.intricatevisions.com/source/newlisp/list.lsp. When I saw Fanda's 'merge', I thought "Hey that's zip and a very pithy definition at that." So, I vote that you strike my name from the Snippets credit and stick Fanda's name there. :-) Cheers, --Rick
Rick, I am glad to see that you read my code :-)
Just to be clear on who gets the credit: I first saw zip-merge-dispose from Nigel Brown and named it 'merge':
http://www.alh.net/newlisp/phpbb/viewtopic.php?p=4582
Here is a dispose that is just a wrapper around the builtin newLISP function transpose.
using args lets you accept any number of lists
viz
> (define (dispose) (transpose (args)))
(lambda () (transpose (args)))
> (dispose '(1 2 3) '(4 5 6))
((1 4) (2 5) (3 6))
> (dispose '(1 2 3) '(4 5 6) '(7 8 9))
((1 4 7) (2 5 8) (3 6 9))
> (setq m '(1 2 3 4 5 6 7))
(1 2 3 4 5 6 7)
> (setq n '(7 6 5 4 3 2 1))
(7 6 5 4 3 2 1)
> (dispose m n)
((1 7) (2 6) (3 5) (4 4) (5 3) (6 2) (7 1))
>
Nigel
So, credit goes to Nigel!
Fanda
PS: Code Snippets show old update time: last updated 2005-12-08
Posted: Mon Nov 06, 2006 2:37 am
by lisp
Fanda wrote:So, credit goes to Nigel!
I call it, if Nigel doesn't want it.
Posted: Mon Nov 06, 2006 4:23 am
by nigelbrown
I'd completely forgotten dispose.
You can see from my contribution re test above I like using (args)
Nigel