平々毎々(アーカイブ)

はてなダイアリーのアーカイブです。

C#でfoldr

何か間違っている気がするなあ。

  • (追記)戻り値のLazyをやめて、Lazyは引数だけにしてみた。
    • (追記)FoldRightに渡す関数の引数はどっちも遅延評価するようにした。ついでに、LazyじゃなくてFuncにしてみた。効果は同じ。
using System;
using System.Collections.Generic;
using System.Linq;

class Program
{
    static void Main(string[] args)
    {
        var source = InfiniteLoop();
        var emptyList = Enumerable.Empty<int>();
        var result = source.FoldRight(emptyList, LazyCons);
        var oneToTen = result.Skip(1).Take(10);
        var sum = oneToTen.FoldRight(0, LazyPlus);
        Console.WriteLine(sum); // 55
    }

    static IEnumerable<int> InfiniteLoop()
    {
        for (var i = 0; ; i++)
            yield return i;
    }

    static IEnumerable<T> LazyCons<T>(Func<T> head, Func<IEnumerable<T>> tail)
    {
        yield return head();
        foreach (var element in tail())
            yield return element;
    }

    static int LazyPlus(Func<int> n, Func<int> m)
    {
        return n() + m();
    }
}

public static class EnumerableEx
{
    public static TResult FoldRight<TSource, TResult>(this IEnumerable<TSource> source, TResult seed, Func<Func<TSource>, Func<TResult>, TResult> lazyFunc)
    {
        // ちゃんとDisposeさせるのは面倒なので、今回はやらない。
        return source.GetEnumerator().FoldRight(seed, lazyFunc);
    }

    public static TResult FoldRight<TSource, TResult>(this IEnumerator<TSource> source, TResult seed, Func<Func<TSource>, Func<TResult>, TResult> lazyFunc)
    {
        if (!source.MoveNext())
            return seed;
        var head = source.Current;
        return lazyFunc(() => head, () => source.FoldRight(seed, lazyFunc));
    }
}