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
Wow, das ist großartig. –
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 :-). –
Habe gerade die nette Optimierung bemerkt, die Sie im elif-Fall dem Algorithmus selbst hinzugefügt haben. –