如何统一这两个浮点函数?

时间:2016-03-03 14:58:05

标签: generics f# numbers type-conversion type-inference

我发现这个neat trick计算一次通过数据的平均值和标准差。我希望这适用于float32floatAgain,我正在努力通过通用数字来做到这一点。

module Seq =
    let inline private avgVarianceReducer toFloat (count, oldM, oldS) x =
        if count = 1 then
            2, x, LanguagePrimitives.GenericZero
        else
            let meanFree = x - oldM
            let newM = oldM + meanFree / (toFloat count)
            count + 1, newM, oldS + meanFree * (x - newM)

    let inline private avgVarianceWith toFloat source =
        match source |> Seq.fold (avgVarianceReducer toFloat) (1, LanguagePrimitives.GenericZero, LanguagePrimitives.GenericZero) with
        | 0, _, _ -> LanguagePrimitives.GenericZero, LanguagePrimitives.GenericZero
        | 1, mean, _ -> mean, LanguagePrimitives.GenericZero
        | n, mean, var -> mean, var / (n - 2 |> toFloat)

    let avgVariance source = source |> avgVarianceWith float
    let avgVariancef source = source |> avgVarianceWith float32

这适用于这两种类型,但我有额外的avgVariancef加上我必须在调用时选择正确的。{/ p>

对我来说,核心问题是在avgVarianceReducer中转换为正确的浮点数,我通过传递正确的转换函数来解决。我尝试了op_Explicit,但失败了。

有人想要更优雅的解决方案吗?

1 个答案:

答案 0 :(得分:3)

你试过FSharpPlus吗? 它包含一个通用数学模块和您正在寻找的通用explicit函数。

以下是您的代码的样子:

#r @"FsControl.dll"
#r @"FSharpPlus.dll"

open FSharpPlus
open FSharpPlus.Operators.GenericMath

module Seq =
    let inline private avgVarianceReducer (count, oldM, oldS) (x:'R) =
        if count = 1 then
            2, x, 0G
        else
            let meanFree = x - oldM
            let newM = oldM + meanFree / explicit count
            count + 1, newM, oldS + meanFree * (x - newM)

    let inline avgVariance source : 'R * 'R =
        match source |> Seq.fold avgVarianceReducer (1, 0G, 0G) with
        | 0, _, _ -> 0G, 0G
        | 1, mean, _ -> mean, 0G
        | n, mean, var -> mean, var / (n - 2 |> explicit)

    // or if you prefer specific functions
    let avgVarianceF32 source : float32 * float32 = avgVariance source
    let avgVarianceF   source : float   * float   = avgVariance source

    // it will work with other types as well
    let avgVarianceD source : decimal * decimal   = avgVariance source

事实上,您不需要函数explicit,您可以使用更具体数字的函数fromIntegral

您还可以浏览库的源代码,并仅提取特定案例所需的代码。