C#3.0で関数プログラミング(モドキ)

関数型言語の真似事をやってみます。(追記)タイトルを修正。

using System;
using System.Collections.Generic;
using System.Functionals;

namespace System.Functionals {
    public delegate T BinaryFunction<T>(T arg1, T arg2);
    public static class Functional {
        // Lispのcarみたいなモノ
        public static T Head<T> (this IEnumerable<T> source) {
            IEnumerator<T> e = source.GetEnumerator ();
            if (e.MoveNext ())
                return e.Current;
            throw new ArgumentException ();
        }

        // Lispのcdrみたいなモノ
        public static IEnumerable<T> Tail<T> (this IEnumerable<T> source) {
            using (IEnumerator<T> e = source.GetEnumerator ()) {
                if (e.MoveNext())
                    while (e.MoveNext ()) yield return e.Current;
            }
        }

        // 畳み込み(左)
        public static T FoldLeft<T> (this IEnumerable<T> source, T acc, 
                BinaryFunction<T> func) {
            T head = source.Head ();
            IEnumerable<T> tail = source.Tail ();
            IEnumerator<T> e = tail.GetEnumerator ();
            if (!e.MoveNext ())
                return func (acc, head);
            else
                return tail.FoldLeft (func (acc, head), func);
        }

        // 畳み込み(右)
        public static T FoldRight<T> (this IEnumerable<T> source, T acc, 
                BinaryFunction<T> func) {
            T head = source.Head ();
            IEnumerable<T> tail = source.Tail ();
            IEnumerator<T> e = tail.GetEnumerator ();
            if (!e.MoveNext ())
                return func (head, acc);
            else
                return func (head, tail.FoldRight (acc, func));
        }

        public static void ForEach<T> (this IEnumerable<T> source, Action<T> action) {
            foreach (T arg in source)
                action (arg);
        }
    }
}

static class Program {
    static void Main () {
        int  nums = new  {1, 2, 3 };
        // 先頭要素 
        Console.WriteLine (nums.Head ());
        // 残りのリスト
        nums.Tail ().ForEach (delegate (int x) { Console.Write ("{0},", x);});
        Console.WriteLine ();

        // 畳み込み(左) = (((0 - 1) - 2) - 3)
        Console.WriteLine ("FoldLeft = {0}", nums.FoldLeft (0, (x, y) => x - y));
        // 畳み込み(右) = (1 - (2 - (3 - 0)))
        Console.WriteLine ("FoldRight = {0}", nums.FoldRight (0, (x, y) => x - y));
    }
}

/* 
1
2,3,
FoldLeft = -6
FoldRight = 2
 */

効率は無視の方向でお願いします。昨日考えていたネタだったんですが、急な仕事でスライド登板です。(^^;

あ、考えてみたらコレ、LINQじゃなくて拡張メソッドの使い方ですね。C#3.0で関数・・・というタイトルにすべきでした。(^^;