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)
如果添加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)
然而,这仍然无法正常工作,因为你是限制currentMean到float32-在一般情况下,编译器需要两个操作的参数具有相同的类型.你可以保持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#编译器自动找出类型约束(因为它们非常难看).