23

同じ長さのリストが 2 つあるのですが、これら 2 つのリストを同時にループすることはできますか?

以下を実行するための正しい構文を探しています

foreach itemA, itemB in ListA, ListB
{
  Console.WriteLine(itemA.ToString()+","+itemB.ToString());
}

これはC#で可能だと思いますか? もしそうなら、これに相当するラムダ式は何ですか?

4

8 に答える 8

27

[編集]: 明確にする; これは、インデクサーを使用できない一般的な LINQ /IEnumerable<T>コンテキストで役立ちます。これは、a: 列挙型に存在せず、b: データを複数回読み取れることを保証できないためです。OP はラムダについて言及しているため、LINQ はそれほど遠くない可能性があります (そうです、LINQ とラムダはまったく同じではないことを認識しています)。

行方不明のZip演算子が必要なようです。あなたはそれを偽装することができます:

static void Main()
{
    int[] left = { 1, 2, 3, 4, 5 };
    string[] right = { "abc", "def", "ghi", "jkl", "mno" };

    // using KeyValuePair<,> approach
    foreach (var item in left.Zip(right))
    {
        Console.WriteLine("{0}/{1}", item.Key, item.Value);
    }

    // using projection approach
    foreach (string item in left.Zip(right,
        (x,y) => string.Format("{0}/{1}", x, y)))
    {
        Console.WriteLine(item);
    }
}

// library code; written once and stuffed away in a util assembly...

// returns each pais as a KeyValuePair<,>
static IEnumerable<KeyValuePair<TLeft,TRight>> Zip<TLeft, TRight>(
    this IEnumerable<TLeft> left, IEnumerable<TRight> right)
{
    return Zip(left, right, (x, y) => new KeyValuePair<TLeft, TRight>(x, y));
}

// accepts a projection from the caller for each pair
static IEnumerable<TResult> Zip<TLeft, TRight, TResult>(
    this IEnumerable<TLeft> left, IEnumerable<TRight> right,
    Func<TLeft, TRight, TResult> selector)
{
    using(IEnumerator<TLeft> leftE = left.GetEnumerator())
    using (IEnumerator<TRight> rightE = right.GetEnumerator())
    {
        while (leftE.MoveNext() && rightE.MoveNext())
        {
            yield return selector(leftE.Current, rightE.Current);
        }
    }
}
于 2008-10-28T06:54:23.327 に答える
11

代わりに、単純な古い for ループで実行する方がはるかに簡単です...

for(int i=0; i<ListA.Length; i++)
{
    Console.WriteLine(ListA[i].ToString() + ", " + ListB[i].ToString());
}
于 2008-10-28T06:56:19.343 に答える
4

明示的に行うことができます。

IEnumerator ListAEnum = ListA.GetEnumerator();
IEnumerator ListBEnum = ListB.GetEnumerator();

ListBEnum.MoveNext();
while(ListAEnum.MoveNext()==true)
{
  itemA=ListAEnum.getCurrent();
  itemB=ListBEnum.getCurrent();
  Console.WriteLine(itemA.ToString()+","+itemB.ToString());
}

少なくともこれ (またはこのようなもの) は、コンパイラが foreach ループに対して行うことです。私はそれをテストしていませんが、列挙子のいくつかのテンプレート パラメーターが欠落していると思います。

List と IEnumerator-Interface から GetEnumerator() を検索するだけです。

于 2008-10-28T07:06:38.887 に答える
2

プレーンな古いforループを使用することをお勧めしますが、配列の長さを変えることを検討する必要があります。それで

for(int i=0; i<ListA.Length; i++)
{
    Console.WriteLine(ListA[i].ToString() + ", " + ListB[i].ToString());
}

に変わることができます

for(int i = 0; i < Math.Min(ListA.Length, ListB.Lenght); i++)
{
    Console.WriteLine(ListA[i].ToString() + ", " + ListB[i].ToString());
}

またはにさえ

    for(int i = 0; i < Math.Max(ListA.Length, ListB.Lenght); i++)
    {
        string valueA = i < ListA.Length ? listA[i].ToString() : "";
        string valueB = i < ListB.Length ? listB[i].ToString() : "";

        Console.WriteLine(valueA+ ", " + valueB);
    }
于 2008-10-28T08:34:47.023 に答える
-1

Senthil Kumar の技術ブログには、 (Python) Itertools for C#の実装をカバーするシリーズがあり、itertools.izip.

Itertools for C# - Cycle and Zipから、任意の数のイテラブル( List<T>だけでなく)に対するソリューションが得られます。各反復で がZip生成されることに注意してください。Array

public static IEnumerable<T[]> Zip<T>(params IEnumerable<T>[] iterables)
{
IEnumerator<T>[] enumerators = Array.ConvertAll(iterables, (iterable) =>   iterable.GetEnumerator());

while (true)
{
   int index = 0;
   T[] values = new T[enumerators.Length];

   foreach (IEnumerator<T> enumerator in enumerators)
   {
       if (!enumerator.MoveNext())
          yield break;

        values[index++] = enumerator.Current;
   }

   yield return values;
}

}

このコードは、すべての iterable の列挙子を取得し、すべての列挙子を前方に移動し、それらの現在の値を配列に累積して、配列を生成します。列挙子のいずれかが要素を使い果たすまで、これを行います。

于 2008-10-28T07:38:07.810 に答える