计算泛型类型的运行平均值

时间:2014-02-02 19:36:26

标签: f# inline typeclass

如果我可以在F#中使用类型类,我正在尝试做一些简单的事情。

我希望能够计算任意类型的运行平均值,我已经定义了加法,标量乘法和标量除法。这是我到目前为止的非编译尝试:

let updateMean<'a when 'a : (static member (*) : 'a -> float32 -> 'a) and 'a : (static member (/) : 'a -> float32 -> 'a) and 'a : (static member (+) : 'a -> 'a -> 'a)> (newObservation : 'a) (currentMean : 'a) (currentNumberOfRecords : int) =
    (newObservation + (currentMean * (currentNumberOfRecords |> float32))) / float32 (1 + currentNumberOfRecords)

这是令人困惑的错误消息:

A type parameter is missing a constraint 'when ( ^a or  ^?766914) : (static member ( + ) :  ^a *  ^?766914 ->  ^?766915)'

1 个答案:

答案 0 :(得分:8)

如果添加inline(这是使用静态成员约束的要求)并将静态变量从'a重命名为^a(这是一种语法),您可以进一步了解由静态解析的类型参数使用)并删除约束的显式规范。然后,编译器将尝试根据代码推断约束的类型(这使得它更有用):

let inline updateMean (newObservation : ^a) (currentMean : ^a) 
                      (currentNumberOfRecords : int) =
    (newObservation + (currentMean * (currentNumberOfRecords |> float32))) / 
       (float32 (1 + currentNumberOfRecords))

但是,这仍然不起作用,因为您将currentMean限制为float32 - 通常,编译器要求运算符的两个参数具有相同的类型。您可以将currentNumberOfRecords保留为相同类型的值 - 然后唯一棘手的部分是添加一个,可以使用LanguagePrimitives.GenericOne完成:

let inline updateMean newObservation currentMean currentNumberOfRecords =
    (newObservation + (currentMean * currentNumberOfRecords)) / 
      (LanguagePrimitives.GenericOne + currentNumberOfRecords)

这很好用,但我可能会使用稍微不同的方法并将总和与总计数保持在一起(然后将它们除以得到均值 - 这可能会有更好的数值属性,因为你避免重复舍入) :

let inline updateState (currentSum, currentCount) newObservation = 
  (currentSum + newObservation, currentCount + 1)

let inline currentMean (currentSum, currentCount) = 
  LanguagePrimitives.DivideByInt currentSum currentCount

诀窍是使用LanguagePrimitives module中的操作,让F#编译器自动找出类型约束(因为它们非常难看)。