# Haskell snippet – recursive average

Greg confessed on #london.pm to having written a average function in Perl in FP style… recursively.

`    sub avg { sum(@_) / @_ }`

which is perfectly fine FP style too. As far as I could understand
it, he was reducing a tuple of sum and count. Though he said he wrote
it just-for-fun, it's not entirely unreasonable if you're using a
list-based language, where you'd otherwise have to traverse the list
twice (in Perl, arrays know how long they are).

Out of curiosity, I tried to check how Haskell defined avg, but it seems that it doesn't.

LoganCapaldo suggested the following definition:

` > import Data.List > avg xs = sum xs / genericLength xs`

We use genericLength because the normal length
function insists on returning an integer, while genericLength returns a
generic Number, which can be divided appropriately. (We'd otherwise
have to write sum xs / (fromIntegral \$ length xs))

The recursive function would work by summing a tuple, so that

`    avgs [1,3,5] => (9,3)`

Once we have that we could divide the tuple using uncurry.

` > avg' = uncurry (/) . avgs`

As we're going to fold the number values into a tuple, we need a function like:

``` > sum_count s (t,c) = (t+s, c+1)
```

after which our definition is easily written as a fold

``` > avgs = foldr sum_count (0,0)
```

Which works just fine. But seeing the tuples, I thought of the comments from Joao and doserj, and thought of (&&&).

` > import Control.Arrow > sum_count s = (+s).fst &&& succ.snd`

And byorgey commented that the .fst and .snd pair can be abstracted away using (***). So I tried

` > sum_count s = (+s) *** succ`

and was really rather surprised that it worked! Admittedly, the
definition is now something that scares me and whose intent is far less
clear than the first naive definition above:

``` > avg2 = uncurry (/) . foldr (\s -> (+s) *** succ) (0,0)
```

For extra bonus obfuscation points, making the lambda expression points-free is left as an exercise to the reader ;-)

Update: mauke pointed out that my Perl avg sub didn't work… added parens to sum (@_).  Looks like naughty me didn't bother testing that code…