how to return an association list

Started by cormullion, November 03, 2006, 06:42:36 AM

Previous topic - Next topic

cormullion

Is it possible to return an association list from a function? Here's a test:


(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... :-)

Lutz

#1
Yes, a small change does it:



(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

cormullion

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


(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).

Lutz

#3
Like this?


(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

cormullion

#4
yes, that looks better .... ;-)



thanks





PS quiet round here isn't it?

Fanda

#5
Lutz wrote:
(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'  :-)


(define (test)
 (letexn (a 1 b 2 c 3 sum (+ a b c))
   '(("a" a) ("b" b) ("c" c) ("sum" sum)) ))


Fanda

Fanda

#6
Another solution:
(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

cormullion

#7
thanks Fanda. I think this is the one I shall use:


(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... ;-)

m i c h a e l

#8
cormullion!



With the addition of the pair function:
(constant (global 'pair) (fn (lst)
  (array-list (array (/ (length lst) 2) 2 lst))))


You can bum it even more:
(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

cormullion

#9
Welcome back, mm. :-) But what does pair do? - why is it using arrays?

m i c h a e l

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

cormullion

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

Fanda

#12
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])


(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

Lutz

#13
The 'array' function was used because it has pairing/grouping already built into it, and it can take a flat list for initialization:


(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:


(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

cormullion

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