简体   繁体   English

F#静态成员类型约束

[英]F# Static Member Type Constraints

I'm trying to define a function, factorize, which uses structural type constraints (requires static members Zero, One, +, and /) similar to Seq.sum so that it can be used with int, long, bigint, etc. I can't seem to get the syntax right, and can't find a lot of resources on the subject. 我正在尝试定义一个函数,factorize,它使用类似于Seq.sum的结构类型约束(需要静态成员Zero,One,+和/),以便它可以与int,long,bigint等一起使用.I似乎无法获得正确的语法,并且无法在该主题上找到很多资源。 This is what I have, please help. 这就是我所拥有的,请帮助。

let inline factorize (n:^NUM) =
    ^NUM : (static member get_Zero: unit->(^NUM))
    ^NUM : (static member get_One: unit->(^NUM))
    let rec factorize (n:^NUM) (j:^NUM) (flist: ^NUM list) = 
        if n = ^NUM.One then flist
        elif n % j = ^NUM.Zero then factorize (n/j) (^NUM.One + ^NUM.One) (j::flist)
        else factorize n (j + ^NUM.One) (flist)
    factorize n (^NUM.One + ^NUM.One) []

Here's how I'd write it: 这是我写的方式:

module NumericLiteralG = begin
  let inline FromZero() = LanguagePrimitives.GenericZero
  let inline FromOne() = LanguagePrimitives.GenericOne
end

let inline factorize n = 
  let rec factorize n j flist =  
    if n = 1G then flist 
    elif n % j = 0G then factorize (n/j) j (j::flist) 
    else factorize n (j + 1G) (flist) 
  factorize n (1G + 1G) [] 

The type inferred for factorize here is way too general, but the function will work as you'd expect. 这里推断出因子分解的类型太笼统了,但是函数会像你期望的那样工作。 You can force a more sane signature and set of constraints if you want by adding explicit types to some of the generic expressions: 如果需要,可以通过向某些泛型表达式添加显式类型来强制使用更合理的签名和约束集:

let inline factorize (n:^a) : ^a list = 
  let (one : ^a) = 1G
  let (zero : ^a) = 0G
  let rec factorize n (j:^a) flist =  
    if n = one then flist 
    elif n % j = zero then factorize (n/j) j (j::flist) 
    else factorize n (j + one) (flist) 
  factorize n (one + one) []

Inspired by kvb's answer using NumericLiterals, I was driven to develop an approach which would allow us to force "sane" type signatures without having to add extensive type annotations. 受kvb使用NumericLiterals的答案的启发,我开始开发一种方法,允许我们强制“理智”类型的签名,而不必添加大量的类型注释。

First we define some helper functions and wrapper type for language primitives: 首先,我们为语言原语定义一些辅助函数和包装类型:

let inline zero_of (target:'a) : 'a = LanguagePrimitives.GenericZero<'a>
let inline one_of (target:'a) : 'a = LanguagePrimitives.GenericOne<'a>
let inline two_of (target:'a) : 'a = one_of(target) + one_of(target)
let inline three_of (target:'a) : 'a = two_of(target) + one_of(target)
let inline negone_of (target:'a) : 'a = zero_of(target) - one_of(target)

let inline any_of (target:'a) (x:int) : 'a =
    let one:'a = one_of target
    let zero:'a = zero_of target
    let xu = if x > 0 then 1 else -1
    let gu:'a = if x > 0 then one else zero-one

    let rec get i g = 
        if i = x then g
        else get (i+xu) (g+gu)
    get 0 zero 

type G<'a> = {
    negone:'a
    zero:'a
    one:'a
    two:'a
    three:'a
    any: int -> 'a
}    

let inline G_of (target:'a) : (G<'a>) = {
    zero = zero_of target
    one = one_of target
    two = two_of target
    three = three_of target
    negone = negone_of target
    any = any_of target
}

Then we have: 然后我们有:

let inline factorizeG n = 
    let g = G_of n
    let rec factorize n j flist =  
        if n = g.one then flist 
        elif n % j = g.zero then factorize (n/j) j (j::flist) 
        else factorize n (j + g.one) (flist) 
    factorize n g.two []

[ Edit : due to an apparent bug with F# 2.0 / .NET 2.0, factorizen, factorizeL, and factorizeI below run significantly slower than factorizeG when compiled in Release-mode but otherwise run slightly faster as expected -- see F# performance question: what is the compiler doing? [ 编辑 :由于F#2.0 / .NET 2.0中明显的错误,factorizen,factorizeL和factorizeI在Release模式下编译时运行速度明显慢于factorizeG,但运行速度略快于预期 - 请参阅F#性能问题:什么是编译器在做什么? ] ]

Or we can take it a few step further (inspired by Expert F#, p.110): 或者我们可以更进一步(受专家F#启发,第110页):

let inline factorize (g:G<'a>) n =   //'
    let rec factorize n j flist =  
        if n = g.one then flist 
        elif n % j = g.zero then factorize (n/j) j (j::flist) 
        else factorize n (j + g.one) (flist) 
    factorize n g.two []

//identical to our earlier factorizeG
let inline factorizeG n = factorize (G_of n) n

let gn = G_of 1  //int32
let gL = G_of 1L //int64
let gI = G_of 1I //bigint

//allow us to limit to only integral numeric types
//and to reap performance gain by using pre-computed instances of G
let factorizen = factorize gn
let factorizeL = factorize gL
let factorizeI = factorize gI

Also, here is an extended version of kvb's NumericLiteralG which allows us to use "2G", "-8G", etc. Though I couldn't figure out how to implement a memoization strategy (though that should be doable for G.any). 另外,这里是kvb的NumericLiteralG的扩展版本,它允许我们使用“2G”,“ - 8G”等。虽然我无法弄清楚如何实现一个memoization策略(虽然这对G.any来说应该是可行的) 。

module NumericLiteralG = 
    let inline FromZero() = LanguagePrimitives.GenericZero
    let inline FromOne() = LanguagePrimitives.GenericOne
    let inline FromInt32(n:int):'a =
        let one:'a = FromOne()
        let zero:'a = FromZero()
        let nu = if n > 0 then 1 else -1
        let gu:'a = if n > 0 then one else zero-one

        let rec get i g = 
            if i = n then g
            else get (i+nu) (g+gu)
        get 0 zero 

Firstly, here is a trivial example that shows how the syntax should look like: 首先,这是一个简单的示例,显示语法应如何:

let inline zero< ^NUM when ^NUM : (static member get_Zero: unit-> ^NUM)> 
    (n:^NUM) = 
  (^NUM : (static member get_Zero : unit -> ^NUM) ())

In some cases, you don't need to write the constraints explicitly (the F# compiler will actually warn you about that if you write the above), because some static members are well-known to the compiler and there are standard functions for using them. 在某些情况下,您不需要显式地编写约束(如果您编写上述内容,F#编译器实际上会警告您),因为编译器已知一些静态成员,并且有使用它们的标准函数。 So, you can use the function and the compiler will infer the constraint: 因此,您可以使用该函数,编译器将推断出约束:

let inline zero (n:^T) = 
  LanguagePrimitives.GenericZero< ^T > 

Unfortunately, this really doesn't help you, because recursive functions cannot be declared as inline (for obvious reasons - the compiler cannot inline the function at compile time, because it doesn't know how many times), so static constraints are probably not powerful enough for your problem. 不幸的是,这对你没有帮助,因为递归函数不能被声明为inline (原因很明显 - 编译器不能在编译时内联函数,因为它不知道多少次),所以静态约束可能不是足够强大的问题。

[ EDIT : This is actually possible for some functions (see kvb's answer)] [ 编辑 :这实际上可以用于某些功能(参见kvb的回答)]

I think you'll need NumericAssociations instead, which were alreaday discussed in this question (these are processed at runtime, so they are slower - but are used to implement for example F# matrix type - the matrix can cache the dynamically obtained information, so it is reasonably efficient). 我认为你需要NumericAssociations这个问题在这个问题中已经讨论过(这些都是在运行时处理的,所以它们比较慢 - 但是用来实现例如F#矩阵类型 - 矩阵可以缓存动态获取的信息,所以它相当有效率)。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM