F# 判别并集的运算符重载
我正在尝试编写一些可以使用标量或向量的数字代码(在本例中,分别是DiffSharp的D和DV类型)。有时我希望能够使用其中一种,因此我为他们定义了一个歧视性联盟:F# 判别并集的运算符重载,f#,operator-overloading,discriminated-union,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 |
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获得梯度,并使其相对于θ参数最大化。基本上,因为您定义的是一个抽象,所以需要根据该抽象定义您的操作。这一成本必须通过它在代码的其他地方为您提供的便利来抵消
你可能想知道的是,F#是否会允许你在你的特定案例中使用样板。除了使用函数
关键字之外,这并不是真的,因为两个分支都在做不同的事情:绑定变量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
示例函数
ll
实际上更通用—它可以处理任何支持它使用的操作的东西,甚至不是D
或DV
的东西。如果使用inline
定义该函数,则可以在以下两种情况下调用该函数:
let inline ll y theta = y*theta-(exp theta)
inline
修饰符允许F#使用静态成员约束,调用函数时所需的成员可以满足静态成员约束(与必须使用.NET运行时提供的内容编译的普通泛型函数不同)
我希望这不会适用于所有代码,因为您将需要一些特定于D
和DV
的操作,但没有通用的F#函数,例如exp
。实际上,您可以使用静态成员约束访问这些约束,尽管这有点麻烦
假设D
和DV
值都有一个返回string
的成员Foo
,您可以编写:
let inline foo (x:^T) =
(^T : (member Foo : string) x)
let inline ll y theta = y*theta-(exp theta)+foo y
您可以通过这样做来减少样板文件:
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 -但是仔细考虑对于二进制运算符的第一个参数是否是一个“<代码>标量< /COD>值”,第二个是“代码> VECT<代码>(反之亦然)——如果不是,那么你的歧视性结合可能不是一个合适的抽象。
我不明白你为什么需要这种结合。与只在D
或DV
上使用运算符相比,它能给你带来什么?@FyodorSoikin我正在尝试编写一个函数,其中一个参数可以是这两种类型中的任何一种,而且似乎联合是实现这一点的常用方法。注意:如果不清楚的话,我是一个相当新的F#程序员。你会如何使用这个函数?它将如何实施?为什么不能用实现来代替函数呢?我已经意识到,重载exp运算符的正确方法是向联合添加一个静态成员,所以我将更新问题。@FyodorSoikin我添加了一个示例。