Interactive Extensionsを自分で実装する – 2.Buffer

Ixを自分で実装する、2回目はBufferです。Bufferのシグネチャはこちら。

static IEnumerable<IList<T>> Buffer<T>(this IEnumerable<T> source, int count);
static IEnumerable<IList<T>> Buffer<T>(this IEnumerable<T> source, int count, int skip);

第1回のForEachは良かったかもしれませんが、テストコードだと動きがわかりにくいと思いますので使用例とConsole出力、それを実現するための実装という順で記載してみます。

使用例:

static void Test_Buffer()
{
    var source = Enumerable.Range(1, 10);
    var buffered = source.Buffer(3);

    foreach (var sequence in buffered)
    {
        foreach (var item in sequence)
        {
            System.Console.Write(item);
        }
        System.Console.WriteLine();
    }
}

buffer_test_1

与えられたシーケンスをBufferの第一引数コずつに区切り、区切った1つをIList<T>として、IList<T>のシーケンスなのでIEnumerable<IList<T>>として返しています。Bufferにはもう一つ、第二引数をとるオーバーロードがあります。こちらはこんな感じです。

static void Test_Buffer2()
{
    var source = Enumerable.Range(1, 10);
    var buffered = source.Buffer(3, 2);

    foreach (var sequence in buffered)
    {
        foreach (var item in sequence)
        {
            System.Console.Write(item);
        }
        System.Console.WriteLine();
    }
}

buffer_test_2

なるほど。第一引数のcountと第二引数のskipの値次第で同じ値が繰り返しでたり、飛ばされたりするんでね。skip=countの場合が、前者実装となっているようです。

 

Buffer…渡されたシーケンスを規定個数毎のIListにまとめたシーケンスを作成する。

実装(Let’s Implement It!)

namespace EmulateInteractiveExtensions
{
    public static class EmulateIxExtensions
    {
        public static IEnumerable<IList<T>> Buffer<T>(this IEnumerable<T> source, int count)
        {
            return source.Buffer(count, count);
        }
        public static IEnumerable<IList<T>> Buffer<T>(this IEnumerable<T> source, int count, int skip)
        {
            if (source == null){
                throw new ArgumentNullException();
            }
            if (count <= 0 || skip <= 0){
                throw new ArgumentOutOfRangeException();
            }

            int index = 0;
            Queue<List<T>> buffers = new Queue<List<T>>();
            foreach (var item in source)
            {
                if (index++ % skip == 0)
                {
                    buffers.Enqueue(new List<T>());
                }
                foreach (var buffer in buffers)
                {
                    buffer.Add(item);
                }
                if (buffers.Count != 0 && buffers.Peek().Count == count)
                {
                    yield return buffers.Dequeue();
                }
            }
            for (int i = 0; i < buffers.Count; i++)
            {
                yield return buffers.Dequeue();
            }
        }
    }
}

同じ値を何度もIListインターフェースで返すことがあるということから、なんらかIListのコレクションが必要だと考えました。IListが生成・出力される順序はFIFOなのでQueueを使っています。Listオブジェクトを無駄に生成したり削除したりしていない点は効率的かなと。

2014.4.1 引数チェックを追加.

広告

コメントを残す

以下に詳細を記入するか、アイコンをクリックしてログインしてください。

WordPress.com ロゴ

WordPress.com アカウントを使ってコメントしています。 ログアウト / 変更 )

Twitter 画像

Twitter アカウントを使ってコメントしています。 ログアウト / 変更 )

Facebook の写真

Facebook アカウントを使ってコメントしています。 ログアウト / 変更 )

Google+ フォト

Google+ アカウントを使ってコメントしています。 ログアウト / 変更 )

%s と連携中