如果我可以在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)'
答案 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#编译器自动找出类型约束(因为它们非常难看)。