pull or push / LINQ or Rx

partitionedはstd::partitionのlazy版です。

一瞬「すげー」と思ったんですが,よく読んでみたら,単に source sequence を二回列挙してるだけっぽくて残念*1.というかまあ二回列挙していいなら全然難しくありませんよね.C# で書いたとしたらこんな感じでしょーか.

static class Make
{
    public static Tuple<IEnumerable<T>, IEnumerable<T>> Partition<T>(
        this IEnumerable<T> source, Func<T, bool> pred)
    {
        return new Tuple<IEnumerable<T>, IEnumerable<T>>(
            source.Where(pred), source.Where(x => !pred(x)));
    }
}

2007/4/2 - 当面C#と.NETな記録』のころからこの話をしているような気がしますが,source sequence を複数回列挙するような LINQ 演算子は邪道というのが私の持論です.
しかしながら source sequence の列挙回数は最大 1 回という条件を満たそうとすると,partition 系の分岐操作で面倒なことになります.このあたりは pull 型の連鎖である LINQ の宿命でしょうか.
以前『パイプライン色々: top down vs. bottom up (2) - NyaRuRuの日記』でまとめましたが,分岐操作は push スタイルの方が向いています.ちょうどタイミング良く .NET Reactive Framework (Rx) が最近ホットなので,興味がある人はチェックしてみると良いでしょう.Rx は push スタイルでイベントの連鎖を記述するため,partition 操作には向いているはずです.

*1:おそらく,oven の range に相当するのは .NET では IEnumerator<T> であって,IEnumerable<T> ではないということなのでしょう.