我目前正在将一些代码从Java移植到F#来处理多维函数。它支持变量维,因此在原始实现中,每个点都表示为双精度数组。代码的关键功能是一个优化例程,它基本上根据某些标准生成一系列点,在这些点评估给定函数并寻找最大值。这适用于任何维度。我需要的操作是:
在F#中我显然也可以以相同的方式使用数组。如果有更好的方法我会徘徊。如果维度是事先修复的,那么显而易见的选择就是使用元组。是否可以在这个动态设置中使用元组?
答案 0 :(得分:3)
不,元组将按维度修复。另请注意,.NET元组是装箱的。如果您正在处理具有较小维度的大型点集合(例如2d点的数组),则使用结构可能会有所帮助。
如果你真的想要推动F#/ .NET优于Java,请看一下泛型。使用泛型编写代码允许编写适用于任何维度的代码,并对不同维度使用不同的表示(比如1-3维的结构和更大维度的向量):
let op<'T where 'T :> IVector> (x: 'T) =
...
这只是相关的,但如果你愿意在很长的路要走,以获得绝对最佳的表现和一般性。大多数项目都不需要,坚持使用最简单的方法。
为了它的乐趣,这是一个如何利用泛型和F#内联的扩展示例:
open System.Numerics
type IVector<'T,'V> =
abstract member Item : int -> 'T with get
abstract member Length : int
abstract member Update : int * 'T -> 'V
let lift<'T,'V when 'V :> IVector<'T,'V>> f (v: 'V) : 'V =
if v.Length = 0 then v else
let mutable r = v.Update(0, f v.[0])
for i in 1 .. v.Length - 1 do
r <- r.Update(i, f v.[i])
r
let inline norm (v: IVector<_,_>) =
let sq i =
let x = v.[i]
x * x
Seq.sum (Seq.init v.Length sq)
let inline normalize (v: 'V) : 'V =
let n = norm v
lift (fun x -> x / n) v
[<Struct>]
type Vector2D<'T>(x: 'T, y: 'T) =
member this.X = x
member this.Y = y
interface IVector<'T,Vector2D<'T>> with
member this.Item
with get (i: int) =
match i with
| 0 -> x
| _ -> y
member this.Length = 2
member this.Update(i: int, v: 'T) =
match i with
| 0 -> Vector2D(v, y)
| _ -> Vector2D(x, v)
override this.ToString() =
System.String.Format("{0}, {1}", x, y)
[<Sealed>]
type Vector<'T>(x: 'T []) =
interface IVector<'T,Vector<'T>> with
member this.Item with get (i: int) = x.[i]
member this.Length = x.Length
member this.Update(i: int, v: 'T) =
let a = Array.copy x
a.[i] <- v
Vector(a)
override this.ToString() =
x
|> Seq.map (fun e -> e.ToString())
|> String.concat ", "
[<Struct>]
type C(c: Complex) =
member this.Complex = c
static member Zero = C(Complex(0., 0.))
static member ( + ) (a: C, b: C) = C(a.Complex + b.Complex)
static member ( * ) (a: C, b: C) = C(a.Complex * b.Complex)
static member ( / ) (a: C, b: C) = C(a.Complex / b.Complex)
override this.ToString() = string c
let v1 = Vector2D(10., 30.)
normalize v1
|> printfn "%O"
let v2 = Vector2D(C(Complex(1.25, 0.8)), C(Complex(0.5, -1.)))
normalize v2
|> printfn "%O"
let v3 = Vector([| 10.; 30.; 50.|])
normalize v3
|> printfn "%O"
请注意norm
和normalize
相当普遍,它们处理专门的2D向量和广义N维向量,以及不同的组件类型,如复数(您可以定义自己的)。泛型和F#内联的使用确保了一般情况下,这些算法使用紧凑表示在特殊情况下表现良好。与Java相比,这就是F#和.NET泛型的亮点,在Java中,您必须创建代码的专用副本才能获得不错的性能。