C# &引用;“转向”;一个IEnumerable<;IEnumerable<;T>&燃气轮机;90度

C# &引用;“转向”;一个IEnumerable<;IEnumerable<;T>&燃气轮机;90度,c#,linq,algorithm,declarative,C#,Linq,Algorithm,Declarative,我要找的是一个基本的操作(我肯定有一个名字,我只是不知道atm)。我有一个矩阵,比如: {1,2,3} {A,N,F} {7,8,9} 我想变异成 {1,A,7} {2,N,8} {3,F,9} (以上只是对象的标识符,而不是实际值。实际对象的类型相同且无序) 我更喜欢声明式解决方案,但速度是一个因素。我将不得不改变很多表格(每分钟10万个单元格),一个慢版本将在关键路径上 然而,我更感兴趣的是一个可读的解决方案。 我正在寻找以下问题的替代解决方案。(我所说的替代方案不是指变化,而是指不同的方法

我要找的是一个基本的操作(我肯定有一个名字,我只是不知道atm)。我有一个矩阵,比如:

{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;

对于(var i=0;i请查看找到的这个扩展方法

//
///交换嵌套序列的行和列。
/// 
///序列中元素的类型。
///源序列。
///交换行和列的序列。
公共静态IEnumerable转置(
这是(不可数的来源)
{
从源中的行返回
从第行的列中选择(
(x,i)=>新的KeyValuePair(i,x))
按列键将列值分组到c中
选择c作为IEnumerable;
}

我不确定性能,但代码看起来很优雅。

您的问题似乎暗示您要修改原始矩阵

如果是这种情况,并且如果您能够将矩阵存储为
IList矩阵
,那么这将仅适用于方形矩阵

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
    }
}
for(int i=0;i
我对这个实现有点怀疑。它对迭代器有局部的副作用,但在我看来逻辑上是干净的。这假设每个序列的长度相同,但应该适用于任何序列。您可以将其视为一个可变长度的方法。它的性能应该比其他答案中的其他链接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();
    }
}
公共静态IEnumerable转置(此IEnumerable源)
{
如果(source==null)抛出新的ArgumentNullException(“source”);
var enumerators=source.Select(x=>x.GetEnumerator()).ToArray();
尝试
{
while(枚举数.All(x=>x.MoveNext())
{
产生返回枚举数。选择(x=>x.Current).ToArray();
}
}
最后
{
foreach(枚举器中的变量枚举器)
枚举数。Dispose();
}
}

好吧,您在这里寻找的是一个转换
T[][]->T[][][]
。有很多
IEnumerabe.Transpose()
解决方案随处可见,但它们都归结为使用临时查找/键循环枚举,而且在大容量上的性能方面,它们还有很多需要改进的地方。您的示例实际上运行得更快(尽管您也可能失去第二个foreach)


首先问“我需要LINQ吗”。你还没有描述转置矩阵的用途,如果速度确实是你关心的问题,你最好远离LINQ/foreach,用老式的方式来做(对于内部)如果有人感兴趣,这里是我的。它的性能与Jeff的相同,但似乎稍快一些(假设那些ToArray()是必要的)。没有可见的循环或临时性,而且更紧凑:

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));
}
公共静态IEnumerable转置(
这是(不可数的来源)
{
返回源
.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));
}
公共静态IEnumerable转置(
这是(不可数的来源)
{
返回源
.Select(a=>a.Select(b=>Enumerable.Repeat(b,1)))
.DefaultIfEmpty(Enumerable.Empty())
.Aggregate((a,b)=>a.Zip(b,Enumerable.Concat));
}
我注意到asker写到矩阵总是正方形的。这个实现(和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;
}
公共静态IEnumerable转置(
这是(不可数的来源)
{
返回源
.Select(a=>a.Select(b=>Enumerable.Repeat(b,1)))
.DefaultIfEmpty(Enumerable.Empty())
.骨料(Zip);
}
公共静态IEnumerable Zip(
首先,我是数数的,
(可数秒)
{
var firstEnum=first.GetEnumerator();
var secondEnum=second.GetEnumerator();
while(firstEnum.MoveNext())
生成返回ZipHelper(firstEnum.Current,secondEnum);
}
私有静态可数ZipHelper(
IEnumerable firstEnumValue,
IEnumerator(第二个枚举)
{
foreach(firstEnumValue中的变量项)
收益回报项目;
secondEnum.MoveNext();
foreach(secondEnum.Current中的变量项)
收益回报项目;
}

这样,每个元素在返回之前都不会被计算。

这是我的基于枚举器的解决方案,它尝试使用
for
来获得比
foreach
/LINQ更快的速度:

public static IEnumerable<IEnumerable<T>> Pivot<T>(this IEnumerable<IEnumerable<T>> src) {
    var enums = src.Select(ie => ie.GetEnumerator()).ToList();
    var initialMoveNext = Enumerable.Repeat(true, enums.Count).ToList();

    for (; ; ) {
        var moveNext = initialMoveNext.ToArray(); // initialize to all true
        var hasMore = false;

        for (int j1 = 0; j1 < enums.Count; ++j1)
            if (moveNext[j1]) {
                moveNext[j1] = enums[j1].MoveNext();
                hasMore = hasMore || moveNext[j1];
            }

        if (!hasMore)
            break;

        IEnumerable<T> subEnum() {
            for (int j1 = 0; j1 < enums.Count; ++j1) {
                if (moveNext[j1])
                    yield return enums[j1].Current;
            }
        }

        yield return subEnum();
    }
    
    for (int j1 = 0; j1 < enums.Count; ++j1)
        enums[j1].Dispose();
}
公共静态IEnumerable Pivot(此IEnumerable src){
var enums=src.Select(ie=>ie.GetEnumerator()).ToList();
var initialMoveNext=Enumerable.Repeat(true,enums.Count.ToList();
对于(;;){
var moveNext=initialMoveNext.ToArray();//初始化为所有true
var hasMore=假;
对于(int j1=0;j1public 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;
}
public static IEnumerable<IEnumerable<T>> Pivot<T>(this IEnumerable<IEnumerable<T>> src) {
    var enums = src.Select(ie => ie.GetEnumerator()).ToList();
    var initialMoveNext = Enumerable.Repeat(true, enums.Count).ToList();

    for (; ; ) {
        var moveNext = initialMoveNext.ToArray(); // initialize to all true
        var hasMore = false;

        for (int j1 = 0; j1 < enums.Count; ++j1)
            if (moveNext[j1]) {
                moveNext[j1] = enums[j1].MoveNext();
                hasMore = hasMore || moveNext[j1];
            }

        if (!hasMore)
            break;

        IEnumerable<T> subEnum() {
            for (int j1 = 0; j1 < enums.Count; ++j1) {
                if (moveNext[j1])
                    yield return enums[j1].Current;
            }
        }

        yield return subEnum();
    }
    
    for (int j1 = 0; j1 < enums.Count; ++j1)
        enums[j1].Dispose();
}