我试图编写一些可以使用标量或向量的数字代码(在这种情况下,它分别来自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参数最大化它。
答案 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
实际上更通用 - 它可以处理支持其使用的操作的任何内容,甚至是非D
或DV
的内容。如果您使用inline
定义它,那么您将能够在两者上调用该函数:
let inline ll y theta = y*theta-(exp theta)
inline
修饰符允许F#使用静态成员约束,调用函数时所需的成员可以满足这些约束(与必须使用.NET运行时提供的编译的普通泛型函数不同)。 p>
我希望这对您的所有代码都不起作用,因为您需要一些特定于D
和DV
的操作,但不具有通用F#函数,例如exp
。你实际上可以使用静态成员约束访问那些,但这有点毛茸茸。
假设D
和DV
值都有成员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
(反之亦然) - 如果不是,那么你的歧视联盟可能不是一个合适的抽象。