Попарная итерация в C # или перечислителе скользящего окна - PullRequest
46 голосов
/ 23 февраля 2009

Если у меня есть IEnumerable вроде:

string[] items = new string[] { "a", "b", "c", "d" };

Я бы хотел пройти через все пары последовательных элементов (скользящее окно размера 2). Который будет

("a","b"), ("b", "c"), ("c", "d")

Мое решение было это

    public static IEnumerable<Pair<T, T>> Pairs(IEnumerable<T> enumerable) {
        IEnumerator<T> e = enumerable.GetEnumerator(); e.MoveNext();
        T current = e.Current;
        while ( e.MoveNext() ) {
            T next = e.Current;
            yield return new Pair<T, T>(current, next);
            current = next;
        }
    }

 // used like this :
 foreach (Pair<String,String> pair in IterTools<String>.Pairs(items)) {
    System.Out.PrintLine("{0}, {1}", pair.First, pair.Second)
 }

Когда я писал этот код, мне было интересно, есть ли уже функции в .NET Framework, которые делают то же самое и делают это не только для пар, но и для кортежей любого размера. ИМХО, должен быть хороший способ сделать этот вид операций со скользящим окном.

Я использую C # 2.0 и могу представить, что в C # 3.0 (с LINQ) есть более (и более приятные) способы сделать это, но в первую очередь меня интересуют решения C # 2.0. Хотя я также буду признателен за решения C # 3.0.

Ответы [ 12 ]

0 голосов
/ 10 января 2010

Модуль F # Seq определяет парную функцию для IEnumerable<T>, но эта функция отсутствует в .NET Framework.

Если бы он уже был в .NET Framework, вместо возврата пар он, вероятно, принял бы функцию селектора из-за отсутствия поддержки кортежей в таких языках, как C # и VB.

var pairs = ns.Pairwise( (a, b) => new { First = a, Second = b };

Я не думаю, что какие-либо ответы здесь действительно улучшат вашу простую реализацию итератора, которая показалась мне наиболее естественной (и постером dahlbyk по внешнему виду !) тоже.

0 голосов
/ 04 марта 2009

Альтернативная Pairs реализация с использованием последней пары для сохранения предыдущего значения:

static IEnumerable<Pair<T, T>> Pairs( IEnumerable<T> collection ) {
  Pair<T, T> pair = null;
  foreach( T item in collection ) {
    if( pair == null )
      pair = Pair.Create( default( T ), item );
    else
      yield return pair = Pair.Create( pair.Second, item );
  }
}

Простая Window реализация (безопасна только для частного использования, если вызывающая сторона не сохраняет возвращенные массивы; см. Примечание):

static IEnumerable<T[]> Window( IEnumerable<T> collection, int windowSize ) {
  if( windowSize < 1 )
    yield break;

  int index = 0;
  T[] window = new T[windowSize];
  foreach( var item in collection ) {
    bool initializing = index < windowSize;

    // Shift initialized window to accomodate new item.
    if( !initializing )
      Array.Copy( window, 1, window, 0, windowSize - 1 );

    // Add current item to window.
    int itemIndex = initializing ? index : windowSize - 1;
    window[itemIndex] = item;

    index++;
    bool initialized = index >= windowSize;
    if( initialized )
      //NOTE: For public API, should return array copy to prevent 
      // modifcation by user, or use a different type for the window.
      yield return window;
  }
}

Пример использования:

for( int i = 0; i <= items.Length; ++i ) {
  Console.WriteLine( "Window size {0}:", i );
  foreach( string[] window in IterTools<string>.Window( items, i ) )
    Console.WriteLine( string.Join( ", ", window ) );
  Console.WriteLine( );
}
...