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

Rob*_*don 5 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)
Run Code Online (Sandbox Code Playgroud)

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

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

Tom*_*cek 8

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

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

然而,这仍然无法正常工作,因为你是限制currentMeanfloat32-在一般情况下,编译器需要两个操作的参数具有相同的类型.你可以保持currentNumberOfRecords相同类型的值 - 然后唯一棘手的部分是添加一个,可以使用LanguagePrimitives.GenericOne以下方法完成:

let inline updateMean newObservation currentMean currentNumberOfRecords =
    (newObservation + (currentMean * currentNumberOfRecords)) / 
      (LanguagePrimitives.GenericOne + currentNumberOfRecords)
Run Code Online (Sandbox Code Playgroud)

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

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

let inline currentMean (currentSum, currentCount) = 
  LanguagePrimitives.DivideByInt currentSum currentCount
Run Code Online (Sandbox Code Playgroud)

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