运营商超载的歧视联盟

时间:2017-05-25 00:17:57

标签: f# operator-overloading discriminated-union

我试图编写一些可以使用标量或向量的数字代码(在这种情况下,它分别来自DiffSharp的D和DV类型)。有时候我希望能够使用其中任何一个,所以我为他们定义了一个有区别的联盟:

type IBroadcastable =
    | Scalar of D
    | Vect of DV

很多运算符已经为这两种类型都重载了,所以要在IBroadcastable上使用它们,我会把这样的代码添加到union中:

static member Exp x = 
    match x with
        | Scalar x -> Scalar (exp x)
        | Vect x -> Vect (exp x)

这似乎非常多余。有没有什么办法可以在联合上使用运算符而不必为它编写新的重载?或者我应该使用不同的模式(即不是一个有区别的联盟)?我想要使​​用此类型的示例:

let ll (y: IBroadcastable) (theta: IBroadcastable) = y*theta-(exp theta)

*-会有更复杂的行为(数组广播),必须自我描述才有意义,但exp运算符很简单,如上所述。这需要是一个函数,因为我希望能够部分应用y参数,使用DiffSharp获取渐变,并相对于theta参数最大化它。

3 个答案:

答案 0 :(得分:2)

从根本上说,既然你要定义一个抽象,你需要根据抽象来定义你的操作。这是一个必须由代码中其他地方提供的便利所抵消的成本。

您可能想知道的是,如果F#允许您在特定情况下切断样板。除了使用function关键字之外,并非真的,因为两个分支实际上都在做不同的事情:绑定变量x的类型不同,并且您将它们包装在不同的联合情况中。如果你真的在做同样的事情,你可以这样写,例如:

type DU =
| A of float * float
| B of float * string
with
    static member Exp = function
        | A (b, _)
        | B (b, _) -> exp b // only write the logic once

答案 1 :(得分:1)

您的示例函数ll实际上更通用 - 它可以处理支持其使用的操作的任何内容,甚至是非DDV的内容。如果您使用inline定义它,那么您将能够在两者上调用该函数:

let inline ll y theta = y*theta-(exp theta)

inline修饰符允许F#使用静态成员约束,调用函数时所需的成员可以满足这些约束(与必须使用.NET运行时提供的编译的普通泛型函数不同)。 p>

我希望这对您的所有代码都不起作用,因为您需要一些特定于DDV的操作,但不具有通用F#函数,例如exp 。你实际上可以使用静态成员约束访问那些,但这有点毛茸茸。

假设DDV值都有成员Foo返回string,您可以写:

let inline foo (x:^T) = 
  (^T : (member Foo : string) x)

let inline ll y theta = y*theta-(exp theta)+foo y

答案 2 :(得分:1)

你可以通过这样的方式减少样板:

type IBroadcastable =
| Scalar of D
| Vect of DV

let inline private lift s v = function
| Scalar d -> Scalar (s d)
| Vect dv -> Vect (v dv)

type IBroadcastable with
    static member Exp b = lift exp exp b
    static member Cos b = lift cos cos b
    ...

如果你想支持二元运算符,你可以定义一个相应的lift2 - 但要仔细考虑二元运算符的第一个参数是Scalar值是否合理成为Vect(反之亦然) - 如果不是,那么你的歧视联盟可能不是一个合适的抽象。