F# F中的全局运算符重载#

F# F中的全局运算符重载#,f#,operators,F#,Operators,我开始定义自己的笛卡尔乘积和矩阵乘法运算符 将矩阵和向量别名为列表: 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 =

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

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

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)
到目前为止还不错。问题是我对
*
的定义抹去了正常定义,尽管我的版本只为列表定义,而列表没有自己的乘法运算符

该文档声明“新定义的运算符优先于内置运算符”。然而,我并没有期望所有的数字化身都消失——静态类型肯定会解决这个问题吗

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


有人知道如何在F#中实现这一点吗?

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

type Matrix = 
  | MatrixData of float list list
  static member (*) (MatrixData m1, MatrixData m2) = (...)
使用
let
定义的运算符在您知道该定义与其他定义不冲突时,或者在小范围内(在函数内)局部定义运算符时非常有用

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

要在您的示例中实现这一点,我必须将类型定义从类型别名(不是独立类型,因此它不能有自己的静态成员)更改为一个区分大小写的联合,它可以有成员,但这可能是一个好主意。

以下是(非惯用)解决方案:

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
type Mult=Mult带
静态成员内联($)(Mult,v1:a列表)=乐趣(v2:b列表)->
v1 |>List.collect(funx->v2 |>List.map(funy->(x,y))):List
静态成员内联($)(Mult,v1:a)=乐趣(v2:a)->v1*v2:a
让内联(*)v1 v2=(Mult$v1)v2

这可能会让您感兴趣:谢谢托马斯。这为我提供了一种很好的矩阵乘法方法,就像MathNet Numerics包所做的那样。我想我会沿着这条路线走下去,但与此同时,有没有一种好方法可以重新定义全局运算符,这样它就可以对
(List)
一对参数执行笛卡尔乘积运算?在中有一个使用中间
Mult
类的答案,但这似乎不完整,因为我在尝试实现它时遇到了类型异常。Gustavo给出了一个非常有趣的答案。关于它,我只有一个问题:他的解决方案是否意味着重定义的
*
操作符每次调用时都执行模式匹配?这会对大规模矩阵乘法产生显著影响吗?@RobLyndon不,它在编译时进行重载解析,然后在调用站点内联函数的代码。所以你不会失去性能。谁在乎它是否是惯用的——也许是关于.NET的。我更喜欢Gustavo的解决方案,它是类型安全的,它更优雅,功能更强大,而且它提供F#中的“类型类”,所有这些都很重要!也许F#应该在未来的版本中得到这种技术更好的支持。“谁在乎它是否是惯用的?”-好吧,所有需要阅读、维护和扩展代码的人……伙计,这太酷了。我在F#interactive中运行了它,然后执行了
让prod=[1..3]*[“你好”;“世界”]
让x=2*5
。结果分别是
[(1,“Hello”);(1,“World”);(2,“Hello”);(2,“World”);(3,“Hello”);(3,“World”)]
和10。是的,如果你想了解更多这方面的信息,我做了一个项目,使用这种技术的改进看起来非常有趣。正如我对Tomas所说的,我认为每次执行重新定义的
*
操作时,您可能都需要为此支付模式匹配操作的费用,但即使是这样,我相信成本也很小。我想知道这是否可以在CUDA monad中工作……您在编译时而不是运行时支付成本。你用的是哪种CUDA Monad?事实上,我正在使用这种技术来定义通用的一元代码。目前我正在使用Alea.cuBase。我很想看看你在一元代码方面的工作。