2013-03-07 39 views
1

我不是C#专家,总LINQ初学者,已经搜查SO和谷歌了一下没有发现如何做到以下几点我有,例如,int[10,10]数组,我怎样才能从它得到一个2D切片?例如,如果所述数组中的值依赖于它们的位置(a [2,3] = 23,a [4,8] = 48等),我想执行以下操作伪代码:采取切片(INT [,])使用LINQ在C#

int[3,3] a_slice = slicer_method(a, 3, 6, 2, 5) // or anything equivalent to this 

> [[ 32, 33, 34], 
    [ 42, 43, 44], 
    [ 52, 53, 54]] 

它没有特别使用LINQ,但我已经看到了LINQ用于我最近遇到的每个类似操作。

回答

4

@JaredPar是正确的,有没有内在的方式做片 - 这么说,你可以通过精心设计了一个扩展方法,这样做:

public static class Ext 
{ 
    public static T[] Slice<T>(this T[] source, int fromIdx, int toIdx) 
    { 
     T[] ret = new T[toIdx - fromIdx + 1]; 
     for(int srcIdx=fromIdx, dstIdx = 0; srcIdx <= toIdx; srcIdx++) 
     { 
      ret[dstIdx++] = source[srcIdx]; 
     } 
     return ret; 
    } 
    public static T[,] Slice<T>(this T[,] source, int fromIdxRank0, int toIdxRank0, int fromIdxRank1, int toIdxRank1) 
    { 
     T[,] ret = new T[toIdxRank0 - fromIdxRank0 + 1, toIdxRank1 - fromIdxRank1 + 1]; 

     for(int srcIdxRank0=fromIdxRank0, dstIdxRank0 = 0; srcIdxRank0 <= toIdxRank0; srcIdxRank0++, dstIdxRank0++) 
     {   
      for(int srcIdxRank1=fromIdxRank1, dstIdxRank1 = 0; srcIdxRank1 <= toIdxRank1; srcIdxRank1++, dstIdxRank1++) 
      { 
       ret[dstIdxRank0, dstIdxRank1] = source[srcIdxRank0, srcIdxRank1]; 
      } 
     } 
     return ret; 
    } 
} 

和测试:

void Main() 
{ 
    var singleArr = new int[]{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; 
    singleArr.Slice(2, 4).Dump(); 
    var doubleArr = new int[,] 
    { 
     { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }, 
     { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }, 
     { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }, 
     { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }, 
     { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }, 
     { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }, 
     { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }, 
     { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }, 
     { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }, 
    }; 
    doubleArr.Slice(2, 4, 2, 4).Dump(); 
} 
+0

这是一个很好的答案,带有示例工作代码,以及一个很好的语法。我会尽快测试一下,谢谢! – heltonbiker 2013-03-07 21:16:49

+0

是的,这种方法唯一的问题是C#中很难支持多维数组的“自动发现”,所以你必须为每个等级组合提供一个变体......我想你可以推广它*有点*,但并不完美。 – JerKimball 2013-03-07 21:21:28

+0

由于更详细的内容以及明确提到这是一个扩展方法(我还没有使用但肯定会去看),我们接受。 – heltonbiker 2013-03-08 12:51:49

4

在CLR上没有办法做到这一点,因为它不支持数组切片的概念。他们最好的你能做的就是创造超过阵列模拟片

+0

是啊,我不是故意的实际片,我只是想“某种方式”与含有较大给定的“内部矩形”的值更小尺寸的二维阵列落得数组,类似于从图像中选择矩形的东西(尽管我的数据与图像无关)。 – heltonbiker 2013-03-07 21:03:45

+0

我编辑了这个问题,以便它不会给出我想要一个数组方法的想法,而是任何返回一个子数组的例程,将较大的数组作为参数。 – heltonbiker 2013-03-07 21:06:28

+1

@heltonbiker只需使用提供的尺寸创建一个新数组,并使用双'for'循环复制信息。到目前为止,这将是最简单和最高效的方法。使用LINQ (在此上下文中)很可能会导致代码混乱和性能下降。一般来说,多维数组往往不能发挥出色。如果这是返回锯齿阵列,那么LINQ可能会有用。 – Servy 2013-03-07 21:09:24

4

你可以尝试这样的事情的包装类型:

public T[,] Slice<T>(T[,] a, int x1, int y1, int x2, int y2) 
{ 
    var result = new T[x2 - x1, y2 - y1]; 
    for (var i = x1; i < x2; i++) 
    { 
     for (var j = y1; j < y2; j++) 
     { 
      result[i - x1, j - y1] = a[i,j]; 
     } 
    } 
    return result; 
} 

sample

+0

我喜欢这个,虽然这是做这件事的微不足道的方法。 +1,我也会看看其他答案。谢谢! – heltonbiker 2013-03-07 21:15:34

+1

平凡很好!特别是当它意味着快速*和*简单! – 2013-03-07 21:17:01

+0

我接受了更详细的JerKimball答案,但您的答案实际上是相同的,具有更清晰的语法优势。非常感谢你! – heltonbiker 2013-03-08 12:52:54

0
public class MyArraySlice<T> where T:struct { 
    public MyArraySlice(T[,] array, int xMin, int xMax, int yMin, int yMax) { 
     Array = array; 
     XMin = xMin; XMax = xMax; 
     YMin = yMin; YMax = yMax; 
    } 

    public T this[int i, int j] { get { 
     if (XMin <= i && i < XMax && YMin <= j && j < YMax) 
      return Array[i+XMin, j+YMin]; 

     throw new ArgumentOutOfRangeException(); 
     } 
    } 

    T[,] Array; 
    int XMin; 
    int XMax; 
    int YMin; 
    int YMax; 
    } 
+0

为什么要添加'where T:struct'?这段代码没有任何内容阻止课程适当。哦,您的索引器中的边界检查已关闭,但这很容易修复。 – Servy 2013-03-07 21:13:04