如何在F#中实现abs,sign等

时间:2010-02-03 15:01:34

标签: f# inline

我发现:

abs -10
abs -10L
两个都工作。所以我想知道F#是如何实现这一点并在源代码中进行搜索的:

    type AbsDynamicImplTable<'T>() = 
    let AbsDynamic x            = AbsDynamicImplTable<_>.Result x

    [<CompiledName("Abs")>]
    let inline abs (x: ^T) : ^T = 
         AbsDynamic x
         when ^T : ^T = absImpl x

我对这些感到困惑。

正如我在abs这样的函数中所知,我们必须将输入与0进行比较,并且对于不同的类型有不同的0。

感谢。

2 个答案:

答案 0 :(得分:13)

要为ChaosPandion发布的代码添加一些解释,F#函数(如abs)的问题在于它们可以使用任何数字类型。没有办法只使用F#/。NET泛型来表达这一点 - 唯一支持的约束是类型参数实现某个接口或具有构造函数,但是对数值类型没有约束。

因此,F#还支持静态约束(类型参数为^a而不是通常的'a)并且这些在编译时使用内联进行处理(这也解释了为什么函数必须是{{ 1}})。您可以使用inline中的内置函数编写具有静态约束的函数,其中包含许多需要一些约束的有用函数:

LanguagePrimitives

请注意,推断出约束 - > let inline half (num: ^a) : ^a = LanguagePrimitives.DivideByInt< (^a) > num 2 ;; val inline half : ^a -> ^a when ^a : (static member DivideByInt : ^a * int -> ^a) > half 42.0;; val it : float = 21.0 > half 42.0f;; val it : float32 = 21.0f 要求类型具有DivideByInt成员,因此我们的函数需要相同的东西(如果它也有这个成员,它将适用于您自己的类型,非常有用!)。

除此之外,DivideByInt的实现还使用了仅在F#库中允许的两个额外技巧 - 它为不同类型指定了不同的代码(在内联时使用)(使用abs )和一个使用when ^a:int = ....成员的后备案例,因此它适用于任何明确列出的类型或具有Abs成员的类型。另一个技巧是Abs函数,它“更改类型”,但不包含任何代码 - 唯一的目的是进行代码类型检查,但这可能非常不安全 - 因此仅使用在F#库中。

答案 1 :(得分:8)

实际上Abs表会称之为:

let inline abs_impl (x: ^a) : ^a = 
     (^a: (static member Abs : ^a -> ^a) (x))
     when ^a : int32       = let x : int32     = retype x in if x >= 0 then x else -x
     when ^a : float       = let x : float     = retype x in if x >= 0.0 then x else -x
     when ^a : float32     = let x : float32   = retype x in if x >= 0.0f then x else -x
     when ^a : int64       = let x : int64     = retype x in if x >= 0L then x else -x
     when ^a : nativeint   = let x : nativeint = retype x in if x >= 0n then x else -x
     when ^a : int16       = let x : int16     = retype x in if x >= 0s then x else -x
     when ^a : sbyte       = let x : sbyte     = retype x in if x >= 0y then x else -x
     when ^a : decimal     = System.Math.Abs(retype x : decimal)