breakが欲しい
例えばこういう処理があるとします。
List.iter (fun i -> print i) a
aには番兵xが含まれておりそこに来たら中断したいとします。
exception Break;; try List.iter (fun i -> if i = x then raise Break else print i) a with | Break -> ()
これは正気の沙汰……というほどでもないですがOCamlでは例外はローカルには定義できないためあまりこういう用途には使いたくないです。例外がローカルに定義さえできればローカルに定義できるみたいですのでガンガン使いたいと思います。
遅延評価があればこれでいいです。
List.iter (fun i -> print i) (takeWhile (( <> ) x) a)
OCamlのリストは残念ながら遅延評価ではありませんので最初にリストを複製することになってしまいますし、xが定数ならいいですが、printしている側で変わる(折り返し有りで10行分出力したら止める等の)条件であればこの手は使えません。
結局、再帰が一番いいです。配列でも同じですね。インデックスでforしても中断できないため、ref使ってwhileにするか、再帰にするか。
let rec loop ls = ( match ls with | i :: lr -> if i <> x then (print i; loop lr) | [] -> () ) in loop a
ところが世の中にはSetだのMapだのといったライブラリがあります。こいつらはiterやfoldは用意されていますが、tが抽象型ですのでパターンマッチできません。
せめて、-> unitではなく-> boolにしてtrueなら中断……まてよ。
# let s = List.fold_right CharSet.add ['A'; 'B'; 'C'; 'D'; 'E'] CharSet.empty;; val s : CharSet.t = <abstr> # CharSet.exists (fun e -> print_char e; false) s;; DCBAE- : bool = false # CharSet.exists (fun e -> print_char e; e = 'B') s;; DCB- : bool = true # CharSet.for_all (fun e -> print_char e; true) s;; DCBAE- : bool = true # CharSet.for_all (fun e -> print_char e; e <> 'B') s;; DCB- : bool = false
使えそうです。やっぱり順番通りにはならないか。
# let m = CharMap.add 'A' 1 CharMap.empty;; val m : int CharMap.t = <abstr> # let m = CharMap.add 'B' 2 m;; val m : int CharMap.t = <abstr> # let m = CharMap.add 'C' 3 m;; val m : int CharMap.t = <abstr> # let m = CharMap.add 'D' 4 m;; val m : int CharMap.t = <abstr> # let m = CharMap.add 'E' 5 m;; val m : int CharMap.t = <abstr> # CharMap.equal (fun i _ -> print_int i; true) m m;; 12345- : bool = true # CharMap.equal (fun i _ -> print_int i; i <> 3) m m;; 123- : bool = false
Mapのほうはキー付きでは無理っぽい。
意外に使えるものを発見してしまいましたが、やっぱ正当なやり方が欲しいです。
例えばCPS風のインターフェースはどうでしょうか。
let rec f next i = (print i; next f) in XXX.iter f a
これですとaとbのループを同時に進めることもできます。
let rec process nextf i nexg j = ( print i; print j; nextf (fun nextf i -> nextg (process nextf i)) ) in XXX.iter (fun nextf i -> YYY.iter (process nextf i) b) a
外部イテレータに比べて凄くまどろっこしいですね。やっぱ柔軟性で言えば外部イテレータがいいです。それ以前にbreakください。
余談ですけれども、foldはよくあるタイプの出力に大変便利だと最近気付きました。
print_string "["; let _ = List.fold_left (fun f e -> if f then print_string "; "; print e; true ) false es; print_string "]"
畳み込み演算に使うよりもこうやって使う方が利用頻度高い気がします。似たような使い方としては、0から始めて+1していってインデックスに使うとか。