Haskell snippet - recursive average

Comments

[this is good]
Pretty! I especially like the use of *** .

There's also a nice one-pass variation where you average the numbers as you go rather than at the end. I use the average-as-you-go trick for anti-aliasing on pixels with 8-bit components, to avoid overflow.
This was discussed a little on Haskell-Cafe in October, including some performance measurements and optimizations.
you might like this short post of mine: http://kraeutler.net/vincent/essays/Folding%20Incremental%20Averages%20in%20Haskell

can't beat that with perl ;-)
ah. a preview might have been nice ;-) anyhow, here's the tinyurl:

http://tinyurl.com/ysr3xa
Shiny - I thought a little about how to do this in one pass, but I'll make sure I give it a go, now that I know it's possible :-)

Thanks, I hadn't seen that. Nice to see that the solution is pretty much identical, too!
Ah, I appear to have missed the rest of the thread: the interesting point there is that doing in one pass may actually be a pessimization (list traversal is cheap anyway), but with some rewriting can be optimized nicely.
Related problem: calculate a list of the averages so far. Now make sure it works for infinite lists. You'll probably want a one-pass algorithm. :)

Extra twist: assume fairly low precision arithmetic, and keep the computed numbers all within the range of sizes of the values seen, to avoid overflow or accuracy loss. Particularly, make sure that if the input numbers are all non-negative, then the auxiliary values are also.

These requirements come up in graphics computations, where pixel values are often represented as three or four 8-bit fixed point, non-negative numbers (24-bit color with optional transparency). Anti-aliasing can be done by sampling the underlying continuous image at several points within a pixel and averaging the results. By calculating a stream of averages-so-far, the results can be shown as they come available. That way, rough results come up quickly and then get nicely smoothed as you watch. For example, see http://conal.net/Pajama .
[this is good]
this code will stack overflow for large lists.

avg' [1..10^6]
*** Exception: stack overflow
*Main
not sure why my last comment cut off. As I was saying, the fix is to use a stricter fold. foldl' from data.list isn't strict enough, however

sumlength = myfoldl' f (0,0)
where f (!accumsum,!accumlength) r = (accumsum+r,accumlength+1)
average xs = (fst a) / (snd a)
where a = sumlength xs
myfoldl' f (!l,!r) [] = (l,r)
myfoldl' f (!l,!r) (x:xs) = ( myfoldl' f q xs )
where q = (l,r) `f` x

is ok.
see also discussion of getting average to work on long lists at

http://groups.google.com/group/fa.haskell/browse_thread/thread/91623361c64d7250/363b382f03e1cc91?lnk=st&q=haskell+average+foldl#363b382f03e1cc91

the discussion is in the middle of a rather long multi-page thread... ctrl-f average...

Post a comment

Already a Vox member? Sign in