2010-05-15 7 views
20

Ich versuche, eine Funktion zu definieren, faktorisieren, die strukturellen Typ Einschränkungen (erfordert statische Mitglieder Zero, One, + und /) ähnlich wie Seq.sum, so dass es mit int verwendet werden kann , long, bigint, etc. Ich kann nicht scheinen, die Syntax richtig zu machen, und kann nicht viele Ressourcen zu diesem Thema finden. Das ist was ich habe, bitte helfe.F # Statische Member Typ Einschränkungen

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) [] 

Antwort

21

Hier ist, wie ich es schreiben würde:

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) [] 

Der Typ gefolgert für factorize hier viel zu allgemein ist, aber die Funktion funktioniert wie man es erwarten würde. Sie können eine vernünftige Signatur und Satz von Einschränkungen erzwingen, wenn Sie durch Hinzufügen von expliziten Typen einige der generischen Ausdrücke wollen:

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) [] 
+0

Wow, das ist großartig. –

+1

Sie haben recht - das funktioniert - eine ziemliche Überraschung für mich :-). Ich bin mir nicht sicher, was hier vor sich geht, weil 'faktorisieren' als generische Funktion kompiliert wird. Es verwendet eine dynamische Implementierung von 'GetZero' (was wahrscheinlich ähnlich ist wie 'NumericAssociations'), aber ich bin nicht sicher, wie das funktioniert (ohne explizite Registrierung der Operationen für Ihren eigenen Typ). Wenn du verstehst, wie das funktioniert, wäre ich sehr an den Details interessiert :-). –

+0

Habe gerade die nette Optimierung bemerkt, die Sie im elif-Fall dem Algorithmus selbst hinzugefügt haben. –

4

Zum einen ist hier ein triviales Beispiel, das zeigt, wie die Syntax sollte wie folgt aussehen:

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

In einigen Fällen Sie nicht über die Einschränkungen schreiben müssen explizit (die F # -Compiler tatsächlich warne dich davor, wenn du das obige schreibst), weil einige statische Member dem Compiler bekannt sind und es Standardfunktionen gibt, um sie zu benutzen. So können Sie die Funktion verwenden, und der Compiler wird die Einschränkung ableiten:

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

Leider ist dies wirklich hilft Ihnen nicht, weil rekursiven Funktionen nicht erklärt werden kann, wie inline (aus offensichtlichen Gründen - der Compiler nicht die Inline kann Funktion zur Kompilierzeit, weil es nicht weiß, wie oft), so statische Einschränkungen sind wahrscheinlich nicht stark genug für Ihr Problem.

:

[EDIT Dies ist tatsächlich möglich, für einige Funktionen (KVB Antwort sehen)] Ich denke, Sie NumericAssociations stattdessen brauchen werden, die alreaday in this question diskutiert wurden (diese werden zur Laufzeit verarbeitet werden, so dass sie langsamer - aber werden verwendet, um zum Beispiel F # -Matrix-Typ zu implementieren - die Matrix kann die dynamisch erhaltenen Informationen zwischenspeichern, so dass es einigermaßen effizient ist).

7

Inspiriert von kvb Antwort mit NumericLiterals wurde ich getrieben, einen Ansatz zu entwickeln, die es uns ermöglichen würden, um Signaturen vom "gesunden" Typ zu erzwingen, ohne umfangreiche Typanmerkungen hinzufügen zu müssen.

Zuerst definieren wir einige Hilfsfunktionen und Wrapper für Sprache Primitiven:

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 
} 

Dann haben wir:

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 [] 

[bearbeiten: aufgrund eines offensichtlichen Fehlers mit F # 2.0 /. NET 2.0, faktorizen, faktorizeL und faktorizeI laufen im Compiliermodus im Release-Modus deutlich langsamer als faktorizeG, laufen aber ansonsten etwas schneller als erwartet - siehe F# performance question: what is the compiler doing?]

Oder wir können es ein paar Schritt weiter (inspiriert von Expert F #, S.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 

Auch hier ist eine erweiterte Version von kvb der NumericLiteralG, die uns „2G“ verwenden können, " -8G "usw.Obwohl ich nicht herausfinden konnte, wie man eine Memoisierungsstrategie implementiert (obwohl das für G.any machbar sein sollte).

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