【问题标题】:Global operator overloading in F#F# 中的全局运算符重载
【发布时间】:2013-10-30 12:36:24
【问题描述】:

我开始为笛卡尔积和矩阵乘法定义自己的运算符。

将矩阵和向量作为列表的别名:

type Matrix = float list list
type Vector = float list

我可以通过编写自己的初始化代码(并获得笛卡尔积)

let inline (*) X Y =
    X |> List.collect (fun x -> Y |> List.map (fun y -> (x, y)))

let createVector size f = 
    [0..size - 1] |> List.map (fun i -> f i)

let createMatrix rows columns f =
    [0..rows - 1] * [0..columns - 1] |> List.map (fun i j -> f i j)

到目前为止一切顺利。问题是我对* 的定义抹去了正常的定义,即使我的版本只为没有自己的乘法运算符的列表定义。

文档http://msdn.microsoft.com/en-us/library/vstudio/dd233204.aspx 声明“新定义的运算符优先于内置运算符”。然而,我没想到所有的数字化身都会被清除——静态类型肯定会解决这个问题吗?

我知道可以定义一个尊重类型的全局运算符,因为 MathNet Numerics 使用 * 进行矩阵乘法。我也想走那条路,这需要打字系统优先考虑矩阵乘法而不是 Matrix 类型的笛卡尔积(它们本身就是列表)。

有谁知道如何在 F# 中做到这一点?

【问题讨论】:

标签: f# operators


【解决方案1】:

这是(非惯用的)解决方案:

type Mult = Mult with
    static member inline ($) (Mult, v1: 'a list) = fun (v2: 'b list) -> 
        v1 |> List.collect (fun x -> v2 |> List.map (fun y -> (x, y))) : list<'a * 'b>
    static member inline ($) (Mult, v1:'a      ) = fun (v2:'a) -> v1 * v2 :'a

let inline (*) v1 v2 = (Mult $ v1) v2

【讨论】:

  • 老兄,这太酷了。我在 F# 交互中运行它,然后执行 let prod = [1..3] * ["Hello"; "World"]let x = 2 * 5。结果分别为[(1, "Hello"); (1, "World"); (2, "Hello"); (2, "World"); (3, "Hello"); (3, "World")]和10。
  • 是的,如果你想了解更多,我做了一个项目,使用了这种技术的改进github.com/gmpl/FsControl
  • 看起来很有趣。正如我对 Tomas 所说,我认为每次执行重新定义的 * 操作时,您可能都必须通过模式匹配操作来为此付出代价,但即使是这样,我相信成本也很小。我想知道这是否可以在 CUDA monad 中工作......
  • 您在编译时而不是运行时支付成本。您使用的是哪个 CUDA Monad?事实上,我正在使用这种技术来定义通用的一元代码。
  • 我目前正在使用 Alea.cuBase。我很想看看你在单子代码方面的工作。
【解决方案2】:

惯用的解决方案是将Matrix定义为新类型,并将运算符添加为静态成员:

type Matrix = 
  | MatrixData of float list list
  static member (*) (MatrixData m1, MatrixData m2) = (...)

使用let 定义的运算符在您知道定义不与其他任何内容冲突时,或者当您在小范围(函数内)本地定义运算符时很有用。

对于自定义类型,最好将运算符定义为静态成员。这种方法的另一个好处是您的类型可以在 C# 中使用(包括运算符)。

要在您的示例中执行此操作,我必须将类型定义从类型别名(它不是独立类型,因此它不能有自己的静态成员)更改为单案例区分联合,它可以有成员 - 但这可能是个好主意。

【讨论】:

  • 谢谢托马斯。这为我提供了一种乘法矩阵的好方法,就像 MathNet Numerics 包一样。我想我会走这条路,但与此同时,有没有一种很好的方法来重新定义全局运算符,以便它可以对 (List&lt;'a&gt;, List&lt;'b&gt;) 一对参数执行笛卡尔积运算?在stackoverflow.com/questions/14121556/… 中有一个使用中间Mult 类的答案,但这似乎不完整,因为我在尝试实现它时遇到了类型异常。
  • Gustavo 给出了一个非常有趣的答案。我对此只有一个问题:他的解决方案是否意味着重新定义的* 运算符每次调用时都会执行模式匹配?这对大规模矩阵乘法有什么明显影响吗?
  • @RobLyndon 不,它在编译时进行重载解析,然后在调用站点内联函数的代码。所以你不会失去性能。
  • 谁在乎它是否是惯用的——也许与 .NET 相关。我更喜欢 Gustavo 的解决方案,它类型安全、更优雅、更实用,并且在 F# 中提供“类型类”,这一切都很重要!也许 F# 在未来的版本中应该得到更好的支持。
  • "谁在乎它是否惯用?" - 每个必须阅读、维护和扩展代码的人......
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-02-18
  • 2011-06-29
  • 1970-01-01
  • 1970-01-01
  • 2017-11-30
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多