在多个列表上迭代

本文关键字:迭代 列表 | 更新日期: 2023-09-27 18:27:40

给定一堆列表,我需要同时对它们进行迭代。假设我有三个:list1list2list3

到目前为止,我发现了以下内容:

foreach (var tuple in list1.Zip(list2, (first, second) => new { object1 = first, object2 = second })
                          .Zip(list3, (first, second) => new { object1 = first.object1, object2 = first.object2, object3 = second }))
{
  //do stuff
}

除非列表的数量不多,否则这很好用,可读性也很强。我知道如何将其进一步扩展到4、5、,。。。。列表,但如果我压缩其中的10个,代码将非常长。有重构它的可能性吗?还是我需要Zip函数以外的其他解决方案?

在多个列表上迭代

在代码生成的帮助下(想想T4),可以产生多达6个类似的重载(因为Tuple限制为7个通用参数)

public static class Iterate
{
    public static IEnumerable<Tuple<T1, T2, T3>> Over<T1, T2, T3>(IEnumerable<T1> t1s, IEnumerable<T2> t2s, IEnumerable<T3> t3s)
    {
        using(var it1s = t1s.GetEnumerator())
        using(var it2s = t2s.GetEnumerator())
        using(var it3s = t3s.GetEnumerator())
        {
            while(it1s.MoveNext() && it2s.MoveNext() && it3s.MoveNext())
                yield return Tuple.Create(it1s.Current, it2s.Current, it3s.Current);
        }
    }
}

有了这个Iterate类,迭代变得非常简单:

foreach(var t in Iterate.Over(
    new[] { 1, 2, 3 }, 
    new[] { "a", "b", "c" }, 
    new[] { 1f, 2f, 3f }))
{
}

这可以进一步推广(类型安全性完全丧失)为:

public static IEnumerable<object[]> Over(params IEnumerable[] enumerables)

为什么不好的旧for循环?

  int n = new int[] {
    list1.Count,
    list2.Count,
    list3.Count,
    // etc.
  }.Min(); // if lists have different number of items
  for (int i = 0; i < n; ++i) {
    var item1 = list1[i]; // if you want an item
    ...
  }

据我所知,真正的问题是要迭代的列表数量未知。我看到的另一个问题是,不能保证所有的列表都有相同的长度。。。对的

如果列表的数量是未知的,元组不会这样做,因为它们将达到8…并且必须在编译时设置。。。

在这种情况下,我建议您不要映射到元组,而是映射到一个简单而古老的结构:矩阵!宽度将是列表的数量(在运行时已知),深度将是最长的列表。您可以使用简单而熟悉的for进行迭代,让编译器优化内存和分配。。。这些代码不仅对C#用户非常可读,而且对任何使用任何编程语言的人来说都非常可读。。。

添加到@AntonGogolev的回答中,关于他的最后一句话。。。如果您不关心类型安全性和性能(对于装箱-开箱),则可以使用object[]:实现枚举器

public static class Iterator
{
   public static IEnumerable<object[]> Enumerate(params IEnumerable[] enumerables)
   {
     var list = new List<object>();
     var enumerators = new List<IEnumerator>();
     bool end = false;
     foreach(var enu in enumerables)
     {
       enumerators.Add(enu.GetEnumerator());
     }
     while(!end)
     {
       list.Clear();
       foreach(var enu in enumerators)
       {
           if(!enu.MoveNext()) { end = true; break; }
           list.Add(enu.Current);                  
       }
       if(!end) yield return list.ToArray();           
     }
   }
}

警告:没有做出任何努力来优化这个代码,它是通过手指编写的:-)

你可以像这样使用它:

var listA = new[] { 1, 2, 3 };
var listB = new[] { "a", "b", "c" };
var listC = new[] { 5f, 6f, 7f };
foreach(var n in Iterator.Enumerate(listA, listB, listC))
{
    foreach(var obj in n)
    {
        Console.Write(obj.ToString() + ", ");
    }
    Console.WriteLine();
}

Fiddle here:https://dotnetfiddle.net/irTY8M