Reputation: 3679
How to write a generic F# function to calculate the mean of a collection? I tried:
let inline mean x =
let length = x |> Seq.length
let sum = x |> Seq.sum
LanguagePrimitives.DivideByInt sum length
This seems working fine for float and decimals but surprisingly doesn't work for int
with an error saying The type 'int' does not support the operator 'DivideByInt'
.
Upvotes: 5
Views: 689
Reputation: 5004
This is one way to create a generic mean
function.
First create a more generic divideByInt
:
type Helper = Helper of int with
static member (&/)(Helper b, a) = a / b
static member (&/)(Helper b, a) = a / (float b)
static member (&/)(Helper b, a) = a / (decimal b)
let inline divideByInt a b = Helper b &/ a
This version of divideByInt
works with float
, decimal
and int
.
Then use it just like in your original solution:
let inline mean x =
let length = x |> Seq.length
let sum = x |> Seq.sum
divideByInt sum length
and test it like this:
mean [5. ; 3.] |> printfn "%A" // 4.0
mean [5 ; 3 ] |> printfn "%A" // 4
Upvotes: 2
Reputation:
Note that summing the sequence then dividing by length is vulnerable to overflow errors. It would be better to use an incremental algorithm, e.g. implement one of the algorithms from this question on math.stackexchange.com.
For example:
let inline mean xs =
xs |> Seq.map float
|> Seq.fold (fun (mean, n) x -> mean + (x-mean) / (float n), n+1) (0.0, 1)
|> fst
Upvotes: 2
Reputation: 36708
To begin with, this function already exists in F#, and is called Seq.average
. If you're doing this just to have that function, you can stop reading now. If you're doing this as a personal learning exercise, read on. :-)
The F# documentation on DivideByInt has the answer here:
If a type supports DivideByInt, the type supports exact division (floating-point division), rather than integer division, which rounds down to the nearest integer result.
Functions like Seq.average work only if the element type supports exact division. If you try to use Seq.average with an integer sequence, you get an error that indicates that the element type must implement DivideByInt. Typically, you can resolve this error by using Seq.averageBy and adding a cast to a floating-point value.
So to get your mean
function to work on integers, you'd need to write it as follows:
let intMean (x : int seq) =
let length = x |> Seq.length
let sum = x |> Seq.map float |> Seq.sum
LanguagePrimitives.DivideByInt sum length
Upvotes: 2