無限リストを途中で打ち切りたい
たとえばある関数 f が単調増加することが分かっている場合、
sum [ a | x <- [1..], let a = f x, a < m ]
みたいに書いて、m 未満の f(x) の和を計算したいのです。
もちろんこのコードは止まりません。処理系は関数が単調増加だなんて知る由もないのですから。
この例のような場合は takeWhile や dropWhile を使って
sum $ takeWhile (< m) [ a | x <- [1..], let a = f x ]
と書けます。m との比較をリスト内包の外に移しただけです。
大抵の場合はこれで済むのですが、打ち切るかどうかの判断をリスト内包の中で行いたいケースがたまにあります。
たとえば終了条件のパラメータがそのまま最終的な要素にならない場合などです。
そのような場合
sum [ b | x <- [1..] , let a = f x b = g a , break_if (a >= m) ]
こんな感じで break_if みたいなものが書けたらなぁ、と思うんです。
手続き型言語で、無限ループしといて後から break するみたいなことがしたいんです。
sum [ b | x <- takeWhile (\x -> f x < m) [1..] , let a = f x b = g a ]
こんな風に、fを二回計算するとかいう無駄なことはもちろんしたくないです。計算量もタイプ量も増えます。こういう場合も勝手にmemoiseされてくれれば楽なんですがそれはちょっと無理があるでしょう。
sum [ b | a <- takeWhile (< m) $ map f [1..] , let b = g a ]
このように「打ち切りチェックに使う値の無限リスト」を括りだす方法だと、終了条件が複数パラメータに依存するような場合に非常にめんどくなります。
そこでbreak_ifが使えれば
sum [ b | x <- [1..] , let a = f x b = g a , break_if (even x && a >= m && b > 0) ]
みたいにすっきり書けるかもしれない。
要するに無限リストから新たなリストを作ろうとすると必ず無限リストになってしまうのが嫌で、map操作とfilter操作に加えてリストを打ち切る操作があると便利なんじゃないかと思った次第です。
他に何か使いまわしのきくうまい書き方ないですかねー?