Iteratorの調査

引き続きIteratorで色々実験。他の言語のスライスの真似事をさせてみました。便利そうなんですが、パフォーマンス的にはちょっと駄目かな。

#region Using directives

using System;
using System.Collections.Generic;
using System.Text;

#endregion

namespace ConsoleApplication1
{
    using L = ListFunc<int>;

    public delegate bool Predicate<T>(T t);

    class ListFunc<T> 
    {
        // begin以降の要素を列挙
         public static IEnumerable<T> Range(IEnumerable<T> g, int begin)
        {
            if (begin < 0)
                throw new IndexOutOfRangeException();

            IEnumerator<T> i = g.GetEnumerator();

            while (begin > 0 && i.MoveNext())
                --begin;
            if (begin != 0)
                throw new IndexOutOfRangeException();

            while (i.MoveNext())
                yield return i.Current;
        }

        // [begin, end)区間の要素を列挙
        public static IEnumerable<T> Range(IEnumerable<T> g, int begin, int end)
        {
            if (begin < 0 || begin >= end)
                throw new IndexOutOfRangeException();

            IEnumerator<T> i = g.GetEnumerator();

            int n = begin;

            while (n > 0 && i.MoveNext())
                --n;
            if (n != 0)
                throw new IndexOutOfRangeException();
            n = end - begin;
            while (n-- > 0 && i.MoveNext())
                yield return i.Current;
        }

        // predを満たす要素を列挙
        public static IEnumerable<T> Find(IEnumerable<T> g, Predicate<T> pred)
        {
            foreach (T t in g)
                if (pred(t))
                    yield return t;
        }

        // IEnumerator<T>の大きさを求める
        public static int Len(IEnumerable<T> g)
        {
            int len = 0;
            IEnumerator<T> i = g.GetEnumerator();
            while (i.MoveNext())
                ++len;
            return len;
        }

        // 指定した位置の値を取得
        public static T At(IEnumerable<T> g, int index)
        {
            foreach (T t in Range(g, index))
                return t;
            throw new IndexOutOfRangeException();
        }
    }

    class Program
    {
        // Iteratorを利用した簡易クイックソート
        static IEnumerable<int> Qsort(IEnumerable<int> g)
        {
            if (L.Len(g) == 0) yield break;

            // 先頭の要素をピボットとする(手抜き)
            int p = L.At(g, 0);
            // ピボットより小さい要素,ピボット,ピボットより大きい値のように並べる
            foreach (int n in Qsort(L.Find(L.Range(g, 1), delegate(int t) { return t < p; }))) 
                yield return n;
            yield return p;
            foreach (int n in Qsort(L.Find(L.Range(g, 1), delegate(int t) { return t >= p; }))) 
                yield return n;
        }

        // 乱数を取得する
        static IEnumerable<int> GetRand(int seed, int cnt)
        {
            Random r = new Random(seed);
            for (int i = 0; i < cnt; ++i )
                yield return r.Next(99);
        }

        static void Main(string[] args)
        {
            // ソート前
            foreach (int n in GetRand(7, 10))
                Console.Write(n.ToString().PadLeft(4));
            Console.WriteLine();

            // ソート後
            foreach (int n in Qsort(GetRand(7, 10)))
                Console.Write(n.ToString().PadLeft(4));
            Console.ReadLine();
        }
    }
}

/* 結果
 37  86  65   5  36  66   4  94  83  84
  4   5  36  37  65  66  83  84  86  94 */

ちょっとしたヘルパー関数をこさえてやると、スクリプト言語っぽいコードが書けます。スクリプト言語のスライスや、STLの<algorithm>、<functional>みたいなライブラリがあるとC#はもっと使いやすくなると思います。誰か作ってくれないかしら。(^^;