誰かが興味を持っているなら、ここに私のものがあります。これは 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));
}
質問者が、行列は常に正方形になると書いていることに気付きました。この実装 (および jeffs) は一度に行全体を評価しますが、行列が正方であることがわかっている場合は、zip 関数をより適切な方法で書き直すことができます。
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;
}
このように、各要素は返されるまで評価されません。