2011-02-18 57 views
8

我在找的是一個基本的操作(我確信有一個名字,我只是不知道atm)。我有這樣的矩陣:「轉動」一個IEnumerable <IEnumerable <T>> 90度

{1,2,3}

{A,N,F}

{7,8,9}

我想變異成

{1,A,7}

{2,N,8}

{3,F,9}

(以上只是對象不是真實值的標識符。實際的物體是相同類型和無序的)

我更喜歡聲明性的解決方案,但速度是一個因素。我將不得不打開幾個表格(每分鐘10萬個單元),而慢速版本將處於關鍵路徑上。

但是我對可讀解決方案更感興趣。 我正在尋找下面的替代解決方案。 (通過替代我不是指的變化,但不同的方法)

var arrays = rows.Select(row => row.ToArray()); 
var cellCount = arrays.First().Length; 
for(var i = 0;i<cellCount;i++){ 
    yield return GetRow(i,arrays); 
} 

IEnumerable<T> GetRow(int i,IEnumerable<T[]> rows){ 
    foreach(var row in rows}{ 
    yield return row[i]; 
    } 
} 

當中兩個幾乎同樣可讀解決方案,我會去的快,但速度之前可讀性去

編輯 這將永遠是一個方形矩陣

+2

它被稱爲轉置(http://en.wikipedia.org/wiki/Transpose) – Frank 2011-02-18 09:35:22

+1

我不認爲它可以得到比這更可讀! – logicnp 2011-02-18 09:39:48

+0

它僅適用於方形矩陣,還適用於非方形? – 2011-02-18 09:42:26

回答

10

我對這個實現有點不瞭解。它對迭代器有局部副作用,但在邏輯上看起來很乾淨。這假定每個序列長度相同,但應該適用於任何序列。您可以將其視爲可變長度Zip()方法。它應該比在其他答案中找到的其他鏈接LINQ解決方案表現得更好,因爲它只使用工作所需的最少操作。沒有使用LINQ可能會更好。甚至可能被認爲是最佳的。

public static IEnumerable<IEnumerable<T>> Transpose<T>(this IEnumerable<IEnumerable<T>> source) 
{ 
    if (source == null) throw new ArgumentNullException("source"); 
    var enumerators = source.Select(x => x.GetEnumerator()).ToArray(); 
    try 
    { 
     while (enumerators.All(x => x.MoveNext())) 
     { 
      yield return enumerators.Select(x => x.Current).ToArray(); 
     } 
    } 
    finally 
    { 
     foreach (var enumerator in enumerators) 
      enumerator.Dispose(); 
    } 
} 
4

看看這個擴展方法。 Linq transpose。 我不確定性能,但代碼看起來很優雅。

1

你的問題似乎暗示要修改原來的矩陣。

如果是這種情況,並且如果您能夠將矩陣存儲爲​​,那麼只有在矩陣矩陣的情況下才能使用。

for(int i = 0; i < matrix.Count; ++i) 
{ 
    for(int j = 0; j < i; ++j) 
    { 
     T temp = matrix[i][j]; 
     matrix[i][j] = matrix[j][i]; 
     matrix[j][i] = temp 
    } 
} 
0

那麼,你在這裏找的是一個轉型T[][] -> T[][]。有大量的解決方案,但它們都歸結爲循環使用臨時查找/鍵的枚舉,並且它們在巨大的音量性能方面留下了很多不足之處。你的例子實際上運行得更快(儘管你也可以放棄第二個foreach)。

首先問「我是否需要LINQ?」。你還沒有描述轉置矩陣的目的是什麼,如果速度確實是你關心的問題,你可能會做得很好,只是遠離LINQ/foreach並以舊式的方式做(用於內部)

0

如果有人感興趣,這是我的。它以與Jeff相同的方式執行,但似乎稍快(假設需要那些ToArrays())。有沒有明顯的循環或臨時工,這是更緊湊:

public static IEnumerable<IEnumerable<T>> Transpose<T>(
    this IEnumerable<IEnumerable<T>> source) 
{ 
    return source 
     .Select(a => a.Select(b => Enumerable.Repeat(b, 1))) 
     .Aggregate((a, b) => a.Zip(b, Enumerable.Concat)); 
} 

如果你需要它來處理空列表過,然後就變成這樣:

public static IEnumerable<IEnumerable<T>> Transpose<T>(
    this IEnumerable<IEnumerable<T>> source) 
{ 
    return source 
     .Select(a => a.Select(b => Enumerable.Repeat(b, 1))) 
     .DefaultIfEmpty(Enumerable.Empty<IEnumerable<T>>()) 
     .Aggregate((a, b) => a.Zip(b, Enumerable.Concat)); 
} 

我注意到,提問者寫道,矩陣將永遠是方形的。此實現(和傑夫斯)將在同一時間評估整個行,但如果我們知道矩陣是方形的,我們可以在一個更合適的方式重寫壓縮功能:

public static IEnumerable<IEnumerable<T>> Transpose<T>(
    this IEnumerable<IEnumerable<T>> source) 
{ 
    return source 
     .Select(a => a.Select(b => Enumerable.Repeat(b, 1))) 
     .DefaultIfEmpty(Enumerable.Empty<IEnumerable<T>>()) 
     .Aggregate(Zip); 
} 

public static IEnumerable<IEnumerable<T>> Zip<T>(
    IEnumerable<IEnumerable<T>> first, 
    IEnumerable<IEnumerable<T>> second) 
{ 
    var firstEnum = first.GetEnumerator(); 
    var secondEnum = second.GetEnumerator(); 

    while (firstEnum.MoveNext()) 
     yield return ZipHelper(firstEnum.Current, secondEnum); 
} 

private static IEnumerable<T> ZipHelper<T>(
    IEnumerable<T> firstEnumValue, 
    IEnumerator<IEnumerable<T>> secondEnum) 
{ 
    foreach (var item in firstEnumValue) 
     yield return item; 

    secondEnum.MoveNext(); 

    foreach (var item in secondEnum.Current) 
     yield return item; 
} 

這樣,每個元素贏得了」要評估直到它返回。

相關問題