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