您可以通过要求您的向量类型(以及向量上的函数)实现某些运算符来执行这些操作。根据您的示例,我想您已经有了+
forVec2
和*
for 向量乘以标量。您可以根据这些运算符编写average
函数,然后它将适用于具有这些运算符的任何类型。
唯一的问题是 F#*
以某种特殊的方式处理,所以如果你有*
type ,你就不能轻易做到这一点float * vector -> vector
。如果您将标量用于向量乘法,它似乎工作正常.*
(同样,您可以添加*.
用于另一个方向)。
以下是我对这些运算符Vec2
的定义:Vec3
type Vec2(a:float, b:float) =
member x.A = a
member x.B = b
static member (.*) (a:float, v:Vec2) =
Vec2(a*v.A, a*v.B)
static member (+) (v1:Vec2, v2:Vec2) =
Vec2(v1.A+v2.A, v1.B+v2.B)
type Vec3(a:float, b:float, c:float) =
member x.A = a
member x.B = b
member x.C = c
static member (.*) (a:float, v:Vec3) =
Vec3(a*v.A, a*v.B, a*v.C)
static member (+) (v1:Vec3, v2:Vec3) =
Vec3(v1.A+v2.A, v1.B+v2.B, v1.C+v2.C)
现在您可以编写average
为使用静态成员约束的内联函数:
let inline average (v1:^V) (v2:^V) =
(^V : (static member (.*) : float * ^V -> ^V) (0.5, v1 + v2))
average (Vec2(1.,1.)) (Vec2(3.,4.))
average (Vec3(1.,1.,1.)) (Vec3(3.,4.,5.))
如果您使用运算符,F# 会自动添加一个约束+
,所以我可以只写v1 + v2
. 该.*
运算符是非标准的,因此我必须显式调用它。
对于您问题的第二部分 - 正如您所指出的,F# 类型不能由具有静态类型约束的其他类型参数化,因此这样做需要更多技巧。您拥有的一种选择是将您需要的操作添加为类型的参数,然后使用一个inline
函数来捕获操作并将它们作为常规函数传递给您的VecFunc
类型。这是一个例子:
type VecFunc<'T1, 'T2>(f:'T1 -> 'T2, mult:float * 'T1 -> 'T1, add:'T2 * 'T2 -> 'T2) =
member x.F = f
member x.Mult = mult
member x.Add = add
static member (.*) (a:float, f:VecFunc<_, _>) =
VecFunc((fun v -> f.F (f.Mult(a, v))), f.Mult, f.Add)
static member (+) (f1:VecFunc<_, _>, f2:VecFunc<_, _>) =
VecFunc((fun v -> f1.Add(f1.F v, f2.F v)), f1.Mult, f1.Add)
let inline vfunc (f:^V -> ^T) =
VecFunc< ^V, ^T>(f,
(fun (a, b) -> (^V : (static member (.*) : float * ^V -> ^V) (a, b))),
(fun (a, b) -> a + b))
let vf = vfunc (fun (v:Vec2) -> v + v)
average vf vf
这种类型检查,但我不确定它是否正确(我不确定向量函数的加法和乘法应该做什么!) - 但无论如何,它可能会帮助您找到正确的方向。