(FS0193,FS1113)F#向量类具有静态分辨类型参数



我正在尝试在f#中实现一个通用的Vector< ^F>类,其中 ^F是向量元素的基础字段类型。这意味着,^F可以是满足添加,减法,乘以和否定的任何东西。

一些示例用户代码看起来像:

    let v = Vector<int>([| 1; 2; 3 |])
    let w = Vector<int>([| 4; 5; 6 |])
    let sum = v + w

因此,在这里我使用类型int,并且可以是满足上述基本操作的任何东西。使用.NET样式通用时,我似乎会得到一些版本,但是我也遇到问题。由于我想无论如何都想使用SRTP,所以我再次走了这条路线:

    type Vector< ^F when ^F : (static member (~-): ^F -> ^F)
                     and ^F : (static member (+): ^F * ^F -> ^F)
                     and ^F : (static member (*): ^F * ^F -> ^F)
               >(_values: ^F[]) =
        let values: ^F [] = _values
        member inline this.Values = values
        member inline this.Dimension = Array.length values
        // Constructs a Vector using given initializer
        static member inline Init (n: int) (initializer: (int -> ^F)) =
            Vector< ^F>(Array.init n (fun i -> initializer (i + 1)))
        member inline this.Item with get (i: int) = values.[i - 1]
        // negate a vector
        static member inline ( ~- ) (a: Vector< ^F>) =
            Vector< ^F>.Init (Array.length a.Values) (fun i -> -a.[i])
        // sum of two vectors
        static member inline ( + ) (a: Vector< ^F>, b: Vector< ^F>): Vector< ^F> =
            Vector< ^F>.Init (Array.length a.Values) (fun i -> a.[i] + b.[i])
        // difference of two vectors
        static member inline ( - ) (a: Vector< ^F>, b: Vector< ^F>): Vector< ^F> =
            Vector< ^F>.Init (Array.length a.Values) (fun i -> a.[i] + (-b.[i]))
        // scale vector by scalar
        static member inline ( * ) (a: ^F, b: Vector< ^F>): Vector< ^F> =
            Vector< ^F>.Init (Array.length b.Values) (fun i -> a * b.[i])

,但是我遇到的错误是动机,例如:

  • FS0193 (警告(:A type parameter is missing a constraint 'when ( ^F or ^?12844) : (static member ( + ) : ^F * ^?12844 -> ^F)'(但也在操作员-*(
  • FS1113 (错误(:The value 'Values' was marked inline but its implementation makes use of an internal or private function which is not-这基本上都是整个地方,并且正在为每个成员属性,成员功能和静态成员功能报告。

(上面剪切的代码是副本兼容,可轻松复制(

我如何解决构造类型的问题,例如向量,元素和操作都有一个静态分辨的类型参数(或仿制(,而没有错误。

srtp错误的诊断很可怕,并且被认为是内部生成的类型ID泄漏到用户空间的错误。可以说,错误消息尚不清楚,但是静态成员并不能满足所有约束。如果将操作员分配到单独的模块中,则类型推理能够照顾应用所有约束(从F#4.6开始(:

type Vector< ^F when ^F : (static member (~-): ^F -> ^F)
                and ^F : (static member (+): ^F * ^F -> ^F)
                and ^F : (static member (*): ^F * ^F -> ^F)
           >(_values: ^F[]) =
    let values: ^F [] = _values
    member inline __.Values = values
    member inline __.Dimension = Array.length values
    // Constructs a Vector using given initializer
    static member inline Init (n: int) (initializer: (int -> ^F)) =
        Vector< ^F>(Array.init n (fun i -> initializer (i + 1)))
    member inline __.Item with get (i: int) = values.[i - 1]
[<AutoOpen>]
module VectorOps =
    let inline ( ~- ) (v: Vector< ^F>) =
        Vector< ^F>.Init (Array.length v.Values) (fun i -> -v.[i])
    let inline ( + ) (a: Vector< ^F>)  (b: Vector< ^F>) =
        Vector< ^F>.Init (Array.length a.Values) (fun i -> a.[i] + b.[i])
    let inline ( - ) (a: Vector< ^F>)  (b: Vector< ^F>) =
        Vector< ^F>.Init (Array.length a.Values) (fun i -> a.[i] - b.[i])
    let inline ( * ) (k: ^F)  (v: Vector< ^F>) =
        Vector< ^F>.Init (Array.length v.Values) (fun i -> k * v.[i])

然后,您可以按预期使用它:

let v1 = Vector<int>([|1;2;3;4;5|])
let v2 = Vector<int>([|1;2;3;4;5|])
let negv1 = -v1
negv1.Values |> Array.iter (fun x -> printfn "%d " x)
let sum  = v1 + v2
sum.Values |> Array.iter (fun x -> printfn "%d " x)
let diff = sum - v2
diff.Values |> Array.iter (fun x -> printfn "%d " x)
let scaled = 3 * diff
scaled.Values |> Array.iter (fun x -> printfn "%d " x)

不幸的是,这涉水到您遇到F#编译器令人讨厌的末端的领域。SRTP并不是真正专为这种编程风格而设计的,尽管它确实有效,但缺乏这种情况的意图确实确实在怪癖和错误消息中泄漏了。

我想为您想做的事建议。

图书馆fsharpplus提供了许多以一致的方式处理此问题的解决方案。

我认为,这种类型的向量的一个不错的功能是应用数学运算符(请参阅标题为"使用应用数学运算符"的代码部分(。

这是您代码的示例:

#r @"FSharpPlus.dll"
open FSharpPlus
open FSharpPlus.Data
open FSharpPlus.Math.Applicative
let v = ZipList [| 1; 2; 3 |]
let w = ZipList [| 4; 5; 6 |]
let sum = v .+. w
// val it : ZipList<int> = ZipList (seq [5; 7; 9])
let mul = v .*. w
// val it : ZipList<int> = ZipList (seq [4; 10; 18])

您也可以针对标量进行操作:

v .* 6 ;;
// val it : ZipList<int> = ZipList (seq [6; 12; 18])
7 +. w ;;
val it : ZipList<int> = ZipList (seq [11; 12; 13])

此处使用的基本抽象是应用函数,因为向量(将Ziplist作为向量(是一种应用函子,它只是可行的。

Ziplist,在FP世界中是有点标准的,但或者您也可以查看ParallelArray应用程序。如果您不喜欢这些名称,则可以复制代码并将定义更改为:

type TVector<'t> =
| Scalar of 't
| Vector of 't array
...
let v = Vector [| 1; 2; 3 |]
v .* 6 ;;
// or
v .*. Scalar 6

最后,如果您不想使用库,则可以将其用作灵感并复制所需的代码。它就在那里,而且有效。

注意

关于您的原始代码,如果您查看我指向您的来源,您会发现它使用了更有功能的方法,例如,它不限制类型级别的类型参数,而是限制了功能。

type Vector< ^F>(_values: ^F[]) =
     let values: ^F [] = _values
     member inline this.Values = values
     member inline this.Dimension = Array.length values
     // Constructs a Vector using given initializer
     static member inline Init (n: int) (initializer: (int -> 'f)) =
         Vector<'f>(Array.init n (fun i -> initializer (i + 1)))
     member inline this.Item with get (i: int) = values.[i - 1]

     // negate a vector
     static member inline ( ~- ) (a: Vector<'f>) =
         Vector<'f>.Init (Array.length a.Values) (fun i -> -a.[i])

请注意,仅在类型级别使用ˆF,而在功能(或静态成员(中使用了不同的类型参数,小写'f

最新更新