2013-03-07 36 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