我正在尝试在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样式泛型时,我似乎可以使用 some 版本,但是我也遇到了问题。由于无论如何我都想使用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])
但是我遇到的错误是精疲力尽,例如:
A type parameter is missing a constraint 'when ( ^F or ^?12844) : (static member ( + ) : ^F * ^?12844 -> ^F)'
(但也适用于运算符-
和*
)The value 'Values' was marked inline but its implementation makes use of an internal or private function which is not
-基本上到处都有,并且针对每个成员属性,成员函数和静态成员函数进行报告。(上面的代码是可复制粘贴的,易于复制)
我该如何解决构造诸如Vector之类的类型的问题,whos元素和操作都具有一个静态解析的类型参数(或泛型),没有错误。
答案 0 :(得分: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并不是专为这种编程风格而设计的,尽管它确实可以工作,但这种情况的意图确实会在怪癖和错误消息中泄漏出来。
答案 1 :(得分:3)
我想为您建议一个现有的解决方案。
FSharpPlus库提供了许多解决方案,可以以一致的方式进行处理。
我认为这类向量的一个不错的功能是Applicative Math Operators(请参见标题为“使用应用数学运算符”的代码部分)。
以下是您的代码示例:
#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])
这里使用的底层抽象是Applicative Functors,因为向量(将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
。