F#类型推断和一些通用的函数参数

时间:2014-01-11 00:29:36

标签: 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

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

我想做的是将参数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类型签名。

2 个答案:

答案 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,(+)和( - )。下表显示了为各种类型隐式定义的静态成员。

接下来提到floatop_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