Help us understand the problem. What is going on with this article?

例外処理と末尾再帰(Re: OCamlの末尾再帰について)

More than 3 years have passed since last update.

この記事はMLアドベントカレンダー23日目の記事です。22日目の記事は autotaker さんのOCamlの末尾再帰についてでした。この記事は autotaker さんの記事の read_ints 関数に対する別解です。前半は、既に autotaker さんの記事を読んでいる人は読み飛ばしても大丈夫です。


例外を投げる関数を呼び出しつつ末尾再帰的な関数を書こうとすると意外なほど落とし穴にはまる。

上記の記事と同じく、 End_of_file 例外が発生するまで read_int で標準入力から int の値を読み込み、読み込んだ値をリストにして返す関数を書こうとすると、最初は次のようなコードを書きがちだ(好みにより、 read_ints の引数に累積値を取るのではなくローカル関数を使うようにしている)。

let read_ints () =
  let rec loop acc =
    try
      let v = read_int () in
      loop @@ v::acc
    with End_of_file -> List.rev acc
  in loop []

だが、これは loop の再帰呼び出しから返ったあとに例外ハンドラをもとに戻すという処理があるため末尾再帰にはならない。

なので、元記事では、例外を option に変換した上で、 trywith の外側で再帰呼び出しをするようにしている。

let read_ints () =
  let rec loop acc =
    let v = try Some (read_int ()) with End_of_file -> None in
    match v with
    | Some v -> loop @@ v::acc
    | None -> List.rev acc
  in loop []

で、これで末尾再帰にはなるのだが、末尾再帰にするためだけに一瞬だけ Some で包んですぐに剥がすのは何だか無駄な気がしてしまう。


実は OCaml 4.02.0 から match 式に exception パターンを書けるようになったので、もう少しスマートに書ける。

let read_ints () =
  let rec loop acc =
    match read_int () with
    | v -> loop @@ v::acc
    | exception End_of_file -> List.rev acc
  in loop []

マッチ対象の式(ここでは read_int ())の評価中のみ例外ハンドラを設定し、例外が発生したら exception パターンと例外を照合し、例外が発生しなければ式の値を exception パターン以外のパターンと照合する。各場合の矢印の右側の式には例外ハンドラは設定されないので、この loop の呼び出しは末尾呼び出しになる。

下のような適当なエントリポイントをつけて、 yes 1 | head -n 10000000 | ./a.out などとしてコンパイルしたプログラムを実行するとスタックがあふれないことが確認できる。

let () =
  read_ints () |> List.length |> string_of_int |> print_endline
Why do not you register as a user and use Qiita more conveniently?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away