【问题标题】:Does F# have generic arithmetic support?F# 是否具有通用算术支持?
【发布时间】:2011-01-14 14:52:28
【问题描述】:

F# 是否存在与 C# 相同的问题,即不能直接将算术运算符用于泛型 T 类型?

你能写一个通用的 Sum 函数来返回任何支持算术加法的值的总和吗?

【问题讨论】:

    标签: generics math f# inline


    【解决方案1】:

    正如 brian 所提到的,有一些对泛型算术的内置支持,您可以使用“静态约束”来自己定义一些泛型函数(尽管这有点受限)。

    除此之外,您还可以使用动态“数字关联”,在函数中使用时会有点慢,但它可以很好地用于定义自己的向量或矩阵类型。这是一个例子:

    #r "FSharp.PowerPack.dll"
    open Microsoft.FSharp.Math
    
    let twoTimesLarger (n:'a) (m:'a) = 
      let ops = GlobalAssociations.GetNumericAssociation<'a>()
      let sel = if ops.Compare(n, m) > 0 then n else m
      ops.Multiply(sel, ops.Add(ops.One, ops.One))
    

    我们首先需要引用包含该功能的 F# PowerPack 库。然后我们定义一个带有签名'a -&gt; 'a -&gt; 'a 的泛型函​​数。第一行动态获取使用类型'a 的数字操作(它本质上使用一些以类型为键的查找表)。然后,您可以使用数值运算对象的方法来执行乘法、加法(MultiplyAdd)和许多其他操作。该函数适用于任何数字:

    twoTimesLarger 3 4  
    twoTimesLarger 2.3 2.4
    twoTimesLarger "a" "b" // Throws an exception!
    

    当您定义自己的数值类型时,您可以定义其数值运算并使用GlobalAssociations.RegisterNumericAssociation 注册它们。相信这也意味着注册操作后你就可以使用内置的F#Matrix&lt;YourType&gt;Vector&lt;YourType&gt;了。

    【讨论】:

      【解决方案2】:

      F# 对此有一些有限的支持。一个好的通用解决方案可能涉及类型类,CLR 通常不支持,特别是 F#。

      F# 使用“静态成员约束”和“内联”函数重载算术运算符。这是使例如+ 操作符在 ints 和 floats 上工作。您可以编写 inline 函数,其实现基于内置的数学运算符并取得一些进展,但一般来说这并非易事。您可以查看例如F# 源代码分发中Array.sum(在 FSharp.Core 中的 array.fs 中)的源代码,与 CTP 一起来感受一下。

      另请参阅此答案的“静态成员约束”和“模拟类型类”部分:

      Functions with generic parameter types

      以及图书馆的各个部分,如

      http://msdn.microsoft.com/en-us/library/ee370581(VS.100).aspx

      http://msdn.microsoft.com/en-us/library/ee340262(VS.100).aspx

      【讨论】:

        【解决方案3】:

        你可以这样做。

        let inline sum<'a when 'a : (static member (+) : 'a -> 'a -> 'a)> a b =
            a + b
        
        let result = sum<int> 3 4
        

        但是,如果我尝试let result = sum 3 4,则会收到错误"type ambiguity inherent in the use of the operator '( + )'"

        【讨论】:

        • 我认为在 F# 中使用静态约束时需要使用 ^a 而不是 'a,但我可能错了...
        • 在这种特殊情况下,您甚至不需要指定泛型参数或约束,因为它们可以被推断出来:let inline sum a b = a + b
        【解决方案4】:

        我所知道的用于执行泛型算术的最佳机制是类型类,遗憾的是 C#、F# 和 .Net 运行时均不支持它。但是,您可以手动模拟它们,如本博文所述:

        Type Classes Are The Secret Sauce

        该技术应该适用于 C# 2.0 或更高版本(使用匿名委托/lambda)。

        人们经常转向界面,但会遇到一些问题

        1. 不能声明现有类型实现接口,因此不能为 int 等内置类型定义该接口的实例。
        2. 接口不能限制方法的其他参数的类型。

        接口声明,对于所有实现,该接口上的所有方法都采用相同的隐式“this”参数类型。如果 Foo 实现了某个接口,那么显然 'this' 参数必须是该实现的 Foo 类型。但是没有办法要求其他方法参数是Foo类型。

        Type classes 允许您(除其他外)对所有方法参数执行这种约束,而不仅仅是第一个参数。

        如前面引用的文章中所述,您可以通过将函数表作为显式参数传递来模拟类型类。

        (社区 wiki:将在此处发布该文章中翻译成 C# 的示例,但由于冗长的解释而没有时间)

        【讨论】:

        • 链接失效,答案未引用文章相关部分。
        猜你喜欢
        • 1970-01-01
        • 2020-09-17
        • 2012-04-01
        • 2015-09-16
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多