受kvb使用NumericLiterals的答案的启发,我被驱使着开发一种方法,使我们能够在不添加大量类型注释的情况下强制实现“合理”的类型签名。
首先,我们定义一些辅助函数和语言原语的包装器类型:
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
}
然后我们有:
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 []
[
编辑:由于F# 2.0/.NET 2.0存在明显的bug,因此在发布模式下编译时,factorizen、factorizeL和factorizeI的运行速度比factorizeG慢得多,但在其他情况下,它们的运行速度略快,如预期所述--请参见
F# performance question: what is the compiler doing?]
或者我们可以进一步推进几步(受Expert 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
此外,这是kvb的NumericLiteralG的扩展版本,允许我们使用"2G","-8G"等。虽然我无法想出如何实现记忆化策略(尽管对于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
factorize
被编译为通用函数。它使用GetZero
的动态实现(可能类似于使用NumericAssociations
),但我不确定这是如何工作的(没有显式注册自己类型的操作)。如果你理解这是如何工作的,我会非常感兴趣了解细节 :-). - Tomas Petricekfactorize
的地方,编译器都会内联整个定义,包括内部递归函数,使用正确的参数类型进行操作。这有帮助吗? - kvbfactorize
是尾递归的(!),这意味着它被翻译成循环并且可以实际内联。这就是让我感到困惑的地方! - Tomas Petricek