使用F#的静态类型参数和编码数字常量

时间:2012-10-22 17:07:09

标签: generics f# numbers bit-manipulation inline

我在F#中编写一个库¹,它提供了一些按位操作,我想确保尽可能多的函数inline让静态类型参数绑定(这允许我编写一个函数)并将其用于int16int32,甚至bignum,只需很少的开销。这条路线肯定无法在某个时刻起作用。所述库提供的一些功能有些复杂。

¹注意:我知道通过公共接口公开内联let绑定的问题。

但是,我想把它延伸到最远的程度。现在,我正试图以这种形式编写population count算法,我遇到了一个问题。我不确定如何编码掩码,例如0x3333...,出现在这些算法中。我可以使用一些额外的技巧来绕过移位常数和类似的东西。

我是否可以使用任何技巧,无论是通过F#的类型推断和静态类型参数,还是通过bit-twiddling,以我想要的方式编写算法?有什么办法可以在静态泛型函数中编码这些常量吗?

一个更模糊的问题:我是否可以依赖一些特定的东西来充分利用静态类型参数,尤其是在数值的背景下?例如,我大量使用GenericOneGenericZero。还有更多这样的事情,我可能错过了吗?

1 个答案:

答案 0 :(得分:5)

首先,您可以使用numeric literals来避免对GenericOneGenericZero的丑陋使用。这是一个简短的例子:

module NumericLiteralG = begin
  let inline FromZero() = LanguagePrimitives.GenericZero
  let inline FromOne() = LanguagePrimitives.GenericOne
  let inline FromInt32 (n:int) =
        let one : ^a = FromOne()
        let zero : ^a = FromZero()
        let n_incr = if n > 0 then 1 else -1
        let g_incr = if n > 0 then one else (zero - one)
        let rec loop i g = 
            if i = n then g
            else loop (i + n_incr) (g + g_incr)
        loop 0 zero 
end

// Usage
let inline ten() = 10G
ten() + 1
ten() + 2L

其次,F#似乎不支持泛型六进制数字文字。一个技巧是使用双反引号,以便您有指示性名称:

let inline shift x = 
    let ``0x33333333G`` = 858993459G
    ((x >>> 2) &&& ``0x33333333G``) + (x &&& ``0x33333333G``)

// Usage
shift 20
shift 20L
shift 20uy

关于数字文字,有一些很好的问题。如果你走这条路,我会提供一些参考资料: