2014-01-11 110 views
2

我有类似下面的功能:F#类型推断,有点泛型函数参数

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 

...或其他任何支持正确运算符(*和 - )的运算符。

我希望做的是限制参数阿尔法浮动(同时保持其他参数通用):

let inline fmingd f' x0 (alpha:float) = ... 

这应该工作,因为载体有运营商(*)使用float - >矢量 - >矢量和运算符( - )与矢量 - >矢量 - >矢量。

但是,只要我这样做的功能变成非泛型..它只接受和返回浮动。它的类型签名变为:

val inline fmingd : f':(float -> float) -> x0:float -> alpha:float -> float 

我能做些什么来保持f',x0和fmingds返回值的一般性,同时限制alpha浮动?

我试图想出以下打动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 

...但在阿尔法·(F” x)的它仍然告诉我:

“这构造使得代码不如 类型注释所指示的类型变量'a被限制为类型 'float'。“

..这导致与上述相同的全浮标型签名。

回答

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 

我回到你原来的错误,但也得到了这样一句:

会员的约束与F#编译器给出了'op_Multiply'这个名字,因为某些.NET类型被这个成员隐式增加了。如果您尝试从自己的代码调用成员约束,则这可能会导致运行时失败。

不幸的是,我不确定如果调用所需的成员times对您而言效果不佳,因为您希望使用其他人的类型。

编辑:

我想可能这个点解决方案的F# language specification

14.5.4.1模拟引起了会员的约束

某些类型的假设隐含地定义静态成员即使实际的CLI元数据类型没有定义这些运算符。该机制用于实现F#库的可扩展转换和数学函数,包括sin,cos,int,float,(+)和( - )。下表显示了为各种类型隐式定义的静态成员。

它接着提到float作为具有op_Multiply的事情之一(即(*))专门定义。

1

当编译器所指出的,问题是在该片段:alpha*(f' x)

这是因为alpha是float和F#乘法具有float作为第一操作数预计另一个float,所以推断float为第二操作数。

如果第二个操作数不是泛型的,即:int,则可以使用如下所示的明确转换:int alpha * (f' x)但无法在F#中表示对泛型类型的泛型op_Explicit的调用,您可以使用member但Ganesh在F#规范中指出的同样的问题也适用于op_Explicit

我在过去有同样的问题,而试图模拟Haskell的泛型数学,特别是通用功能fromIntegerI 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