【问题标题】:how are abs, sign etc implemented in F#在 F# 中如何实现 abs、sign 等
【发布时间】:2010-02-03 15:01:33
【问题描述】:

我发现:

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。

谢谢。

【问题讨论】:

  • 您不必将 X 与 0 进行比较即可找到其绝对值 - 您可以将其与 -X 进行比较。
  • @Simon 似乎是个好主意。但是,你的方法不正确。考虑 x=-2147483648。
  • 是的,好点子 - 虽然我怀疑在这种情况下你会得到同样的异常来评估 -X 因为 abs() 无论如何都应该抛出。

标签: f# inline


【解决方案1】:

为了对 ChaosPandion 发布的代码添加一些解释,像 abs 这样的 F# 函数的问题在于它们可以处理任何数字类型。仅使用 F#/.NET 泛型无法表达这一点 - 唯一受支持的约束是类型参数实现某个接口或具有构造函数,但对数字类型没有约束。

因此,F# 还支持静态约束(类型参数是 ^a 而不是通常的 'a)并且这些是在编译时使用内联处理的(这也解释了为什么函数必须是 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 成员,所以我们的函数需要同样的东西(如果它也有这个成员,它将适用于您自己的类型,这非常有用! )。

除此之外,abs 的实现还使用了仅在 F# 库中允许的两个附加技巧 - 它为不同类型(使用 when ^a:int = ....)指定不同的代码(内联时使用)和回退case,它使用Abs 成员,因此它适用于任何显式列出的类型或具有Abs 成员的类型。另一个技巧是 retype 函数,它“更改类型”,但不包含任何代码 - 唯一目的是对代码进行类型检查,但这可能非常不安全 - 所以这只用于 F#图书馆。

【讨论】:

    【解决方案2】:

    其实 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) 
    

    【讨论】:

    • +1 用于实际实现,但我想我的答案的第一个版本是正确的。
    猜你喜欢
    • 2010-12-14
    • 1970-01-01
    • 2020-06-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-02-03
    • 2015-12-17
    • 1970-01-01
    相关资源
    最近更新 更多