Arc Forumnew | comments | leaders | submitlogin
Improve this function?
4 points by bOR_ 5980 days ago | 11 comments
I wanted to make a function that calculates the average of a list of that is a mixture of integers and lists of integers. for example:

  (= test (list 1 2 3 (list 3 4 5) (list 3 4 (list 1 2 3))))
would resolve to 1+2+3+(resolves to 4)+(resolves to 3) / 5 = 13 / 5.

I wrote a function for that which works, but I'm curious how you'd write it. Often there's something to learn of alternative implementations, especially since I'm a lisp-newbie.. =).

  (def dothis (li)
     (with (sum 0 size (len li))
        (map1 (fn (_) 
           (if (is (type _) 'cons) (= sum (+ sum (dothis _))) (= sum (+ sum _))))
           li)
        (/ sum size)))
works!


7 points by drcode 5980 days ago | link

  (def ravg (x)
    (or (errsafe:avg:map ravg x) x))

-----

4 points by skenney26 5980 days ago | link

  (def rec (f base xs)
    ((rfn r (xs)
       (if (atom xs)
           (base xs)
           (f (map r xs))))
     xs))

  arc> (= test (list 1 2 3 (list 3 4 5) (list 3 4 (list 1 2 3))))
  (1 2 3 (3 4 5) (3 4 (1 2 3)))
  arc> (rec avg idfn test)
  13/5

  arc> (rec avg [* _ 2] test)
  26/5
  arc> (rec [apply + _] [* _ 3] test)
  93

-----

8 points by rincewind 5980 days ago | link

  (def rec-avg (lst)
     (if acons.lst
         (/ (apply + (map rec-avg lst)) len.lst)
         lst))

-----

2 points by bOR_ 5979 days ago | link

Thanks for the improvements!

@rincewind - nice one. (apply + is indeed one of the most obvious improvements I should familiarize myself. Familiar with it in ruby (inject), but in combination with recursions, I fell back to the simpler (with. acons is a good pointer as well for me.

@drcode - solution: "try to take the average of each element in a list. If that doesn't work, return the element". Didn't know about avg, which is an obvious improvement. errsafe and or are whole new operators to me, which are going to be interesting to try and learn to use. Nice!.

@skenney26 - might still be a bit too difficult for me.. the function rec takes 3 arguments (an operator, a base whose purpose I'm not sure of, and the list). rec creates an internal function called 'r'. If xs is an atom (i.e., not a list) then 'r' returns (base xs), otherwise it maps, calling r recursively.

Ok. think I get what base is for :).. it allows you to modify the numbers before you subject them to f. Nice!

Thanks all of you!

-----

1 point by skenney26 5979 days ago | link

Actually, I'm with you on rec. Even after writing it my head was struggling to fully understand it. Complex recursion is tough to grasp; i wish there was a way of visualizing recursive calls that was similar to expanding a macro. Does anyone know if there's a way of doing that?

-----

1 point by tokipin 5980 days ago | link

flat should be useful here. eg:

  (def avg2 (lst)
      (avg (flat lst)))
though i'm away from the lab and can't verify it right now

-----

2 points by fallintothis 5980 days ago | link

I thought at at first, but that won't produce the same answers. Take his test case:

  '(1 2 3 (3 4 5) (3 4 (1 2 3)))
Using the desired method, it's:

  (/ (+ 1
        2
        3
        (/ (+ 3 4 5) 3)
        (/ (+ 3 4 (/ (+ 1 2 3) 3)) 3))
     5) ;= 13/5
But by flattening the list, we get:

  (/ (+ 1 2 3 3 4 5 3 4 1 2 3) 11) ;= 31/11

-----

1 point by tokipin 5980 days ago | link

ah, didn't see the 'resolved' thingie

-----

2 points by offby1 5980 days ago | link

topkin's thingy doesn't give the same result. Try it and see.

-----

1 point by silence 5979 days ago | link

I am learning arc too and came up with this:

  (def foo (l)
     (avg:map [if (isa _ 'int) _ (foo _)] l))
drcode's version is even better. I didn't know about errsafe either. Out of curiouslity, is there a performance impact to using errsafe in this way since there will be an error for each integer in the list?

-----

2 points by drcode 5979 days ago | link

I don't know if I'd actually write something like that in a real program... not because of performance (which isn't good, but merely by a constant factor, so it's not an issue IMHO) but because the errsafe could hide other errors...

that said, the performance, in theory, might actually be better in some cases: lists (but not atoms) will be processed with one less condition.

-----