访问F#中的GetSlice方法

时间:2016-11-30 12:44:03

标签: arrays multidimensional-array f# functional-programming pattern-matching

我正在为F#中的矩阵类实现切片表达式,这意味着我必须实现三个GetSlice方法。我的问题是通过利用核心库中的现有功能来获得有效的方法。这三种方法的签名如下:

member GetSlice : idx1:int * start2:int option * end2:int option -> IMatrix
member GetSlice : start1:int option * end1:int option * idx2:int -> IMatrix
member GetSlice : start1:int option * end1:int option * 
                  start2:int option * end2:int option -> IMatrix

考虑这三者的最终结果。矩阵类有一个名为float[,]的私有entries字段,用于保存矩阵数据。最自然的实现是这样的:

member this.GetSlice (?start1 : int, ?end1 : int, ?start2 : int, ?end2 : int) =
    let newEntries = entries.GetSlice(?start1 = start1, ?end1 = end1,
                                      ?start2 = start2, ?end2 = end2)
    new Matrix(newEntries),

只使用GetSlice实现的float[,]功能。但是,这不会编译,因为GetSlice扩展方法不可用(至少没有我打开的命名空间)。我原以为这个方法默认在FSharp.Core中可用,但实际情况并非如此。基于float[,]的切片表达式的手动实现可能看起来像这样

member this.GetSlice (?start1 : int, ?end1 : int, ?start2 : int, ?end2 : int) =
    let newEntries = match (start1, end1, start2, end2) with
        | Some(sta1), Some(end1), Some(sta2), Some(end2) -> entries.[sta1 .. end1, sta2 .. end2]
        | Some(sta1), Some(end1), Some(sta2), None -> entries.[sta1 .. end1, sta2 .. ]
        (...)
    new Matrix(entries),

这将需要16个模式案例,因此有些不方便。是否有更简单的选项,可能基于某种方式获得GetSlice的{​​{1}}扩展方法的访问权限?

2 个答案:

答案 0 :(得分:6)

您可以使用defaultArg为这些选项指定值,然后在数组中将它们切片:

member __.GetSlice (?start1 : int, ?end1 : int, ?start2 : int, ?end2 : int) =
  let start1, end1, start2, end2 =
    defaultArg start1 0,
    defaultArg end1 (Array2D.length1 entries - 1),
    defaultArg start2 0,
    defaultArg end2 (Array2D.length2 entries - 1)

  new Matrix (entries.[start1 .. end1, start2 .. end2])

答案 1 :(得分:0)

扩展方法只是语法糖,使静态方法看起来像实例方法。

如果您在GetSlice扩展方法上点击“转到定义”(我猜你看到它在另一个项目中工作了吗?),您将能够看到它背后的“真实”静态方法。它将类似于:

public static class SomeMatrixUtilityLibrary.ExtensionMethods
{
    public static float[,] GetSlice(this float[,] array2d,
                                    int start1, 
                                    int end1,
                                    int start2,
                                    int end2)
   {
        //... 
   }
}

静态方法在F#中运行得很好,你只需要正常调用它而不是通过扩展语法。