Inspirado en respuesta usando NumericLiterals del KVB, me llevaron a desarrollar un enfoque que nos permita para forzar firmas de tipo "cuerdas" sin tener que agregar anotaciones de tipo extensas.
En primer lugar, definir algunas funciones de ayuda y tipo de contenedor para primitivas del lenguaje:
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
}
entonces tenemos:
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 []
[Editar: debido a un fallo aparente con F # 2.0 /. NET 2.0, factorizen, factorizeL y factorizeI a continuación se ejecutan significativamente más lento que factorizeG cuando se compila en modo Release, pero si no se ejecuta un poco más rápido como se esperaba - ver F# performance question: what is the compiler doing?]
O podemos tomar un par de paso más (inspirado por el experto F #, p.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
Además, aquí es una versión extendida de NumericLiteralG de kvb la que nos permite utilizar "2G", " -8G ", etc.Aunque no pude encontrar la manera de implementar una estrategia de memorización (aunque eso debería ser factible para 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
Guau, eso es increíble. –
Tienes razón, esto funciona, es una gran sorpresa para mí :-). No estoy seguro de lo que está sucediendo aquí, porque 'factorize' se compila como una función genérica. Utiliza la aplicación dinámica de 'GetZero' (que es probablemente similar al uso de' NumericAssociations'), pero no estoy seguro de cómo funciona esto (sin registro explícito de las operaciones para su propio tipo). Si entiendes cómo funciona esto, estaría bastante interesado en los detalles :-). –
Acabo de notar la buena optimización que ha agregado en el caso elif al algoritmo mismo. –