🐪
OCamlインタプリタを起動しっぱなしで、再帰関数を書くと不思議な動きをする
「プログラミングの基礎」(浅井健一著) のp84の問題9.4を解いていた時に起きた出来事。
動作環境
Linux/Ubuntu 22.04.4 LTS
OCaml 4.13.1
まず、以下の通り書き、保存する。ファイル名は"09-04_length.ml"とする。
(* 目的:受け取った整数のリストの長さを返す。 *)
(* length : int list -> int *)
let length lst = match lst with
[] -> 0
| first :: [] -> 1
| first :: rest -> 1
let test1 = length [] = 0
let test2 = length [1] = 1
let test3 = length [2; 1; 6; 4; 7] = 5
OCamlインタプリタを起動し、記述したファイルを読み込む。
# # use "09-04_length.ml";;
val length : 'a list -> int = <fun>
val test1 : bool = true
val test2 : bool = true
val test3 : bool = false
test3は「5」と返ることを期待しているが、「1」が変えるためfalseになる。
「5」を返すためには再帰呼び出しにすればよい。
再帰呼び出しするように以下の通り修正する。ただし、再帰関数であることを示す「rec」はここでは書かないでおく。
(* 目的:受け取った整数のリストの長さを返す。 *)
(* length : int list -> int *)
let length lst = match lst with <--- 「rec」キーワードをわざと書かない。
[] -> 0
| first :: [] -> 1
| first :: rest -> length rest <---- ここを修正した
let test1 = length [] = 0
let test2 = length [1] = 1
let test3 = length [2; 1; 6; 4; 7] = 5
起動しっぱなしのocamlインタプリタで記述したファイルを読み込む。
以下のような「recを書き忘れているよ」というワーニング(というかヒント)が出るはず
Hint: If this is a recursive definition,
you should add the 'rec' keyword on line 3
だが、「recを書き忘れているよ」が出ない。なぜ?
# use "09-04_length.ml";;
val length : 'a list -> int = <fun>
val test1 : bool = true
val test2 : bool = true
val test3 : bool = false
そのままインタプリタで「length [2;1;6;4;7]」を呼び出してみるとなぜか「2」を返す。なんで?
length [2;1;6;4;7];;
- : int = 2
この状態で再度ファイルを読み込み、その後「length [2;1;6;4;7]」を呼び出してみると
#use "09-04_length.ml";;
val length : 'a list -> int = <fun>
val test1 : bool = true
val test2 : bool = true
val test3 : bool = false
length [2;1;6;4;7];;
- : int = 3
#use "09-04_length.ml";;
val length : 'a list -> int = <fun>
val test1 : bool = true
val test2 : bool = true
val test3 : bool = false
length [2;1;6;4;7];;
- : int = 4
今度は「3」を返し、もう一度同じことを行うと今度は「4」を返す。
なぜなんだー。
なお、
OCamlインタプリタをいったん終了し、再度起動してファイルを読み込むと期待通り
#use "09-04_length.ml";;
File "09-04_length.ml", line 6, characters 25-31:
6 | | first :: rest -> 1 + length rest
^^^^^^
Error: Unbound value length
Hint: If this is a recursive definition,
you should add the 'rec' keyword on line 3
となるので安心してください。
Discussion