IEnumerable(T) から n 個ずつ要素を取得


遺伝暗号表や base64 のように,連続したいくつかの要素を取得しながら処理したいということがあります。たとえば AUGUUAAUCAACCAA を AUG UUA AUC AAC CAA と 3 つずつ取得するような感じです[A]。これを拡張メソッドにしました。

public static IEnumerable<IEnumerable<TSource>> TakeBy(this IEnumerable<TSource> source, int count)
{
   if (source == null)
   {
      throw new ArgumentNullException("source");
   }
   if (count < 1)
   {
      throw new ArgumentOutOfRangeException("count");
   }
   using (var enumerator = source.GetEnumerator())
   {
      var values = new TSource[count];
      while (enumerator.MoveNext())
      {
         values[0] = enumerator.Current;
         for (var index = 1; index < count; index++)
         {
            if (!enumerator.MoveNext())
            {
               throw new InvalidOperationException("The number of elements is insufficient.");
            }
            values[index] = enumerator.Current;
         }
         yield return values;
      }
   }
}

public static IEnumerable<IEnumerable<TSource>> TakeOrDefaultBy(this IEnumerable<TSource> source, int count)
{
   if (source == null)
   {
      throw new ArgumentNullException("source");
   }
   if (count < 1)
   {
      throw new ArgumentOutOfRangeException("count");
   }
   using (var enumerator = source.GetEnumerator())
   {
      var values = new TSource[count];
      while (enumerator.MoveNext())
      {
         values[0] = enumerator.Current;
         var index = 1;
         for (; index < count; index++)
         {
            if (!enumerator.MoveNext())
            {
               break;
            }
            values[index] = enumerator.Current;
         }
         for (; index < count; index++)
         {
            values[index] = default(T);
         }
         yield return values;
      }
   }
}

TakeBy は要素が不足している場合に例外を投げますが, TakeOrDefaultBy は例外を投げずに不足分にデフォルト値を補います。長さ count の配列を作成していますが,メソッドを抽出すれば配列は作成しないことも可能だと思います。地味な実装ですが,うまい人が書けばもっと美しく実装できるのでしょうか。

脚注

  1. 翻訳して最初の開始コドン (AUG) を除くと LINQ となります。 []