我有以下功能:
let inline fmingd f' x0 alpha =
let rec step x i =
if i >= 100 then x
else step (x - alpha*(f' x)) (i+1)
step x0 0
它足够通用,可以使用浮点数:
let f' x = 4.*x + 3.
let fminx = fmingd f' 0. 0.1
...和(MathNet.Numeric)向量:
let f' (x:Vector<float>) = (x.[0], x.[1]) |> fun (x, y) -> vector [4.*x + 3.; 8.*y + 5.]
let fminx = fmingd f' (vector [0.;0.]) 0.1
...或其他支持正确运算符的其他运算符(*和 - )。
我想做的是将参数alpha限制为float(同时保持其他参数通用):
let inline fmingd f' x0 (alpha:float) = ...
这应该有效,因为向量具有带有float的运算符(*) - &gt;矢量 - &gt;向量和运算符( - )与向量 - &gt;矢量 - &gt;矢量。
但是一旦我这样做,该函数就变成非泛型的..它只接受并返回浮点数。它的类型签名变为:
val inline fmingd : f':(float -> float) -> x0:float -> alpha:float -> float
我可以做些什么来保持f',x0和fmingds返回值泛型,同时将alpha限制为float?
我尝试通过提出以下内容来打动F#编译器:
let inline fmingd (f':^a -> ^a) (x0:^a) (alpha:float) : ^a
when ^a : (static member ( * ) : float * ^a -> ^a) and
^a : (static member ( - ) : ^a * ^a -> ^a) =
let rec step x i =
if i >= 100 then x
else step (x - alpha*(f' x)) (i+1)
step x0 0
...但是在alpha *(f'x)它还在告诉我:
“这种结构导致代码的通用性低于 类型注释。类型变量'a已被约束为类型 '浮动'。“
..这导致与上面相同的all-float类型签名。
答案 0 :(得分:3)
看起来(*)被特别对待。将代码更改为此工作:
let inline fmingd (f':^a -> ^a) (x0:^a) (alpha:float) : ^a
when ^a : (static member times : float * ^a -> ^a) and
^a : (static member ( - ) : ^a * ^a -> ^a) =
let rec step x i =
if i >= 100 then x
else step (x - (^a : (static member times : float * ^a -> ^a) (alpha, f' x))) (i+1)
step x0 0
然后当我尝试这个时:
let inline fmingd (f':^a -> ^a) (x0:^a) (alpha:float) : ^a
when ^a : (static member ( * ) : float * ^a -> ^a) and
^a : (static member ( - ) : ^a * ^a -> ^a) =
let rec step x i =
if i >= 100 then x
else step (x - (^a : (static member ( * ): float * ^a -> ^a) (alpha, f' x))) (i+1)
step x0 0
我回到原来的错误,但也得到了这个:
具有名称'op_Multiply'的成员约束由F#编译器赋予特殊状态,因为某些.NET类型是使用此成员隐式扩充的。如果您尝试从自己的代码中调用成员约束,这可能会导致运行时失败。
不幸的是,我不确定如果你想要使用其他人的类型,调用所需的成员times
是否会对你有效。
编辑:
我认为这可能是由F# language specification
中的这一点引起的14.5.4.1成员约束解的模拟
假定某些类型隐式定义静态成员,即使类型的实际CLI元数据未定义这些运算符。该机制用于实现F#库的可扩展转换和数学函数,包括sin,cos,int,float,(+)和( - )。下表显示了为各种类型隐式定义的静态成员。
接下来提到float
是op_Multiply
(即( * )
)特别定义的事情之一。
答案 1 :(得分:1)
正如编译器指出的那样,问题在于这个片段:alpha*(f' x)
这是因为alpha是float
,而在f#中,具有float
作为第一个操作数的乘法需要另一个float
,因此它会为第二个操作数推断float
。
如果第二个操作数不是泛型的,即:int,你可以使用这样的显式转换:int alpha * (f' x)
但是没有办法表达对泛型类型的泛型op_Explicit
的调用F#,您可以尝试使用成员约束,但Ganesh在F#规范中指出的同样问题也适用于op_Explicit
。
我在尝试模拟Haskell的泛型数学时遇到了同样的问题,特别是泛型函数fromInteger
但是I solved it using overloading,你可以使用相同的技术来创建泛型fromFloat
函数或使用通用convert
函数作为我刚添加到F#+
示例代码:
#r @"FsControl.Core.dll"
#r @"FSharpPlus.dll"
open FSharpPlus
let inline fmingd (f':^a -> ^a) (x0:^a) (alpha:float) : ^a =
let rec step x i =
if i >= 100 then x
else step (x - convert alpha * (f' x)) (i+1)
step x0 0