动态压缩 n 列表
dynamically zip n Lists
我在编译时看到了 zip
这样的解决方案,已知数量的 List
s 大于两个 List
s:
public static class MyFunkyExtensions
{
public static IEnumerable<TResult> ZipThree<T1, T2, T3, TResult>(
this IEnumerable<T1> source,
IEnumerable<T2> second,
IEnumerable<T3> third,
Func<T1, T2, T3, TResult> func)
{
using (var e1 = source.GetEnumerator())
using (var e2 = second.GetEnumerator())
using (var e3 = third.GetEnumerator())
{
while (e1.MoveNext() && e2.MoveNext() && e3.MoveNext())
yield return func(e1.Current, e2.Current, e3.Current);
}
}
}
如果您有 List<List<>>
并且想要动态压缩它们,正确的代码是什么? 注意列表的数量在编译时是未知的。我不想创建 ZipFour、ZipFive 等...
是这样的吗?
public static IEnumerable<TResult> ZipAll<T, TResult>(
IEnumerable<IEnumerable<T>> lists,
Func<IEnumerable<T>, TResult> func)
{
var enumerators = lists.Select(l => l.GetEnumerator()).ToArray();
while(enumerators.All(e => e.MoveNext()))
{
yield return func(enumerators.Select(e => e.Current));
}
foreach (var enumerator in enumerators)
{
enumerator.Dispose();
}
}
这假设每个 list/enumerable 的类型参数是相同的(即你想在 List<List<int>>
之类的东西上调用它。如果不是这种情况,你需要使用非-generic IEnumerable
而不是(并在最后摆脱 foreach
,因为非通用 IEnumerable
不是一次性的)。
我没有对此进行过大量测试;有兴趣看看评论者可能会在其中戳出什么样的洞。
编辑:
- 正如 MineR 调用的那样,此实现不会捕获示例实现中
using
语句的效果。有几种不同的方法可以修改它以使用 try/finally(或多个 try/finallys),具体取决于您希望如何处理可能在 GetEnumerator
、MoveNext
、Current
和 Dispose
。
- 此外,虽然您可以压缩无限长度的枚举,但
Zip
在概念上需要有限数量的这些枚举。如果 lists
是 ICollection<IEnumerable<T>>
类型可能会更正确。如果 lists
是无限的,这将抛出一个 OutOfMemory
异常。
- 经过一番讨论:一个具体要求是能够在选择器函数中使用索引器。这可以通过使第三个参数
Func<IList<T>, TResult>
代替Func<IEnumerable<T>, TResult>
,并在enumerators.Select(e => e.Current)
. 中添加一个ToArray()
来实现。
如果不知道你想如何压缩你的列表,很难写出一个精确的解决方案,但你可以做如下的事情:
List<int> ListOne = new List<int> { 1, 2, 3, 4, 5 };
List<int> ListTwo = new List<int> { 123, 12, 3243, 223 };
List<int> ListThree = new List<int> { 23, 23, 23, 23, 23, 23 };
var AllLists = new List<List<int>> { ListOne, ListTwo, ListThree };
var result = AllLists.Aggregate((acc, val) => acc.Zip(val, (init, each) => init + each).ToList());
你传递给 Zip 函数的函数显然是你想要处理 Zip 的,在这种情况下,整数只是简单地加在一起,但字符串可以连接等。聚合函数将用于枚举所有列表并将结果合并到一个容器中,在本例中是一个列表。
我在编译时看到了 zip
这样的解决方案,已知数量的 List
s 大于两个 List
s:
public static class MyFunkyExtensions
{
public static IEnumerable<TResult> ZipThree<T1, T2, T3, TResult>(
this IEnumerable<T1> source,
IEnumerable<T2> second,
IEnumerable<T3> third,
Func<T1, T2, T3, TResult> func)
{
using (var e1 = source.GetEnumerator())
using (var e2 = second.GetEnumerator())
using (var e3 = third.GetEnumerator())
{
while (e1.MoveNext() && e2.MoveNext() && e3.MoveNext())
yield return func(e1.Current, e2.Current, e3.Current);
}
}
}
如果您有 List<List<>>
并且想要动态压缩它们,正确的代码是什么? 注意列表的数量在编译时是未知的。我不想创建 ZipFour、ZipFive 等...
是这样的吗?
public static IEnumerable<TResult> ZipAll<T, TResult>(
IEnumerable<IEnumerable<T>> lists,
Func<IEnumerable<T>, TResult> func)
{
var enumerators = lists.Select(l => l.GetEnumerator()).ToArray();
while(enumerators.All(e => e.MoveNext()))
{
yield return func(enumerators.Select(e => e.Current));
}
foreach (var enumerator in enumerators)
{
enumerator.Dispose();
}
}
这假设每个 list/enumerable 的类型参数是相同的(即你想在 List<List<int>>
之类的东西上调用它。如果不是这种情况,你需要使用非-generic IEnumerable
而不是(并在最后摆脱 foreach
,因为非通用 IEnumerable
不是一次性的)。
我没有对此进行过大量测试;有兴趣看看评论者可能会在其中戳出什么样的洞。
编辑:
- 正如 MineR 调用的那样,此实现不会捕获示例实现中
using
语句的效果。有几种不同的方法可以修改它以使用 try/finally(或多个 try/finallys),具体取决于您希望如何处理可能在GetEnumerator
、MoveNext
、Current
和Dispose
。 - 此外,虽然您可以压缩无限长度的枚举,但
Zip
在概念上需要有限数量的这些枚举。如果lists
是ICollection<IEnumerable<T>>
类型可能会更正确。如果lists
是无限的,这将抛出一个OutOfMemory
异常。 - 经过一番讨论:一个具体要求是能够在选择器函数中使用索引器。这可以通过使第三个参数
Func<IList<T>, TResult>
代替Func<IEnumerable<T>, TResult>
,并在enumerators.Select(e => e.Current)
. 中添加一个
ToArray()
来实现。
如果不知道你想如何压缩你的列表,很难写出一个精确的解决方案,但你可以做如下的事情:
List<int> ListOne = new List<int> { 1, 2, 3, 4, 5 };
List<int> ListTwo = new List<int> { 123, 12, 3243, 223 };
List<int> ListThree = new List<int> { 23, 23, 23, 23, 23, 23 };
var AllLists = new List<List<int>> { ListOne, ListTwo, ListThree };
var result = AllLists.Aggregate((acc, val) => acc.Zip(val, (init, each) => init + each).ToList());
你传递给 Zip 函数的函数显然是你想要处理 Zip 的,在这种情况下,整数只是简单地加在一起,但字符串可以连接等。聚合函数将用于枚举所有列表并将结果合并到一个容器中,在本例中是一个列表。