我发现:
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。
感谢。
答案 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)