(unfold p f g seed tail-gen) == (if (p seed) (tail-gen seed) (cons (f seed) (unfold p f g (g seed))))これをC#に直訳する。ただし、tail-genは無視する(optionalなので)。
public static IEnumerable<U> Unfold<T, U> ( Predicate<T> p, Func<T, U> f, Func<T, T> g, T seed ) { if (p(seed)) return Enumerable.Empty<U>(); else return Cons ( f(seed), Unfold(p, f, g, g(seed)) ); } public static IEnumerable<T> Cons<T> ( T car, IEnumerable<T> cdr ) { yield return car; foreach (T element in cdr) yield return element; }これって、結局こういうこと。(そして、C#には末尾最適化がないのだから、必ずこう書くべきだ。)
public static IEnumerable<U> Unfold<T, U> ( Predicate<T> p, Func<T, U> f, Func<T, T> g, T seed ) { for (T i = seed; !p(i); i = g(i)) yield return f(i); }だったら、パラメータの順番を変えたほうが、Unfoldを呼び出すときにIntelliSenseが効いてうれしい。
public static IEnumerable<U> Unfold<T, U> ( T initial, Predicate<T> until, Func<T, T> next, Func<T, U> convert ) { for (T i = initial; !until(i); i = next(i)) yield return convert(i); }C#にわざわざUnfoldを用意する必然性はあるのだろうか。自分でも考えてみる。
そうだな、匿名メソッドやラムダ式ではyieldが使えないけど、それで困るようなシチュエーションならFoldとかUnfoldとかその類のメソッドがあると便利。
でもより重要なのは、きっとDLinqやPLinq(んー、でもUnfoldを並列処理するのは難しいか)みたいな世界に簡単に対応できるというポテンシャルなんだろう。そういう意味では、今現在での意味は薄いのか。
0 件のコメント:
コメントを投稿