【问题标题】:dynamically zip n Lists [duplicate]动态压缩 n 个列表 [重复]
【发布时间】:2019-04-16 02:23:53
【问题描述】:

我在编译时看到了类似zip 的解决方案,已知数量的Lists 大于两个Lists:

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&lt;List&lt;&gt;&gt; 并且想要动态压缩它们,那么正确的代码是什么? 请注意,列表的数量在编译时是未知的。我不想创建 ZipFour、ZipFive 等...

【问题讨论】:

  • 那么组合的结果是什么?另一个List&lt;List&lt;&gt;&gt;,但带有压缩值(我猜有点旋转)?
  • T1 = T2 = T3 = Tn 也是?

标签: c# ienumerable


【解决方案1】:

这样的?

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&lt;List&lt;int&gt;&gt; 之类的东西上调用它。如果不是这种情况,您需要使用非泛型 IEnumerable (最后去掉foreach,因为非通用IEnumerable不是一次性的)。

我没有对此进行大量测试;有兴趣看看评论者可能会在其中戳出什么样的漏洞。

编辑:

  • 正如 MineR 所指出的,此实现不会捕获示例实现中 using 语句的效果。有几种不同的方法可以修改它以使用 try/finally(或多个 try/finallys),具体取决于您希望如何处理GetEnumeratorMoveNextCurrent 和 @ 中可能发生的异常987654330@.
  • 此外,虽然您可以压缩无限长的枚举,但Zip 在概念上需要有限数量的枚举。如果listsICollection&lt;IEnumerable&lt;T&gt;&gt; 类型,它可能会更正确。如果lists 是无限的,这将引发OutOfMemory 异常。
  • 经过一些讨论:一项特定要求是能够在选择器函数中使用索引器。这可以通过将第三个参数设为Func&lt;IList&lt;T&gt;, TResult&gt; 而不是Func&lt;IEnumerable&lt;T&gt;, TResult&gt; 并将ToArray() 添加到enumerators.Select(e =&gt; e.Current) 来实现。

【讨论】:

  • 也许是尝试/终于,但是是的。
  • 肯定的;我的主要目标是扩展示例代码及其所有行为(无论好坏)。
  • 啊,现在我看到需要 try/finally 才能获得 usings 的效果。
  • nlawalker 这很接近,但是 Func 需要采用与列表相同数量的参数。如果有三个列表,则它必须是 Func 四个等。lambda 也必须动态创建。我猜它可能是 如果 T 是动态创建的元组<...>
  • 如果列表数量在编译时未知,则 lambda 不能采用与列表相同数量的参数。
【解决方案2】:

如果不确切知道你想如何压缩你的列表,很难写出一个准确的解决方案,但你可以做如下的事情:

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 函数的函数显然是您想要处理 Zipping 的函数,在这种情况下,整数只是简单地添加在一起,但字符串可以连接等。聚合函数将用于枚举所有列表虽然并将结果合并到一个容器中,在本例中是一个列表。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2017-05-04
    • 1970-01-01
    • 2020-06-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-11-23
    相关资源
    最近更新 更多