たとえば (position "a" ["b"; "a"; "c"; "a"]) は、1を返す。
position の具体的な実装は以下の通りである。
let rec position (x : string) (venv : string list) : int = match venv with | [] -> failwith "no matching variable in environment" | y::venv2 -> if x=y then 0 else (position x venv2) + 1
いよいよコンパイラの本体である。まず、Dowek & Levy の教科書のコンパイル方式を引用する。
これから上記の変換(Compilation of PCF)の定義を解読しよう。
|let x=1 in let y=2 in x+y|_空 = Pushenv, |1|_空, Extend, |let y=2 in x+y|_x, Popenv = Pushenv, Ldi 1, Extend, Pushenv, |2|_x, Extend, |x+y|_(x,y), Popenv, Popenvとなる。ここで、x+y という式の変換のときの e が (x,y) になっていること に注目してほしい。(ここで (x,y) と書いたが、かっこはグルーピングの目的 のためにつけた記号であり、無意味である。) これは、x+y という式を変換するとき、束縛変数(実行時の環境に格納される変数たち)は、 x と y であること、また、x が1番目で、y が2番目であることを表している。 そこで、x+y の中の x を変換するときは、Search 0 に変換され、 y を変換するときは、Search 1 となる。(1番目が0, 2番目が1である。) よって、
|let x=1 in let y=2 in x+y|_空 = Pushenv, Ldi 1, Extend, Pushenv, Ldi 2, Extend, Search 0, Push, Search 1, Add, Popenv, Popenvとなる。このように、変数が(実行時の)環境の中の何番目に格納されているか を調べる目的のため、|M|_e の e を使ったのである。
(letrec f x = e1 in e2) === (let f = (fixfun f x -> e1) in e2)
[I_Pushenv; I_Ldi 1; I_Extend; I_Pushenv; I_Ldi 2; I_Extend; I_Search 0; I_Push; I_Search 1; I_Add; I_Popenv; I_Popenv]となる。
ここで e1[e3/x] は、式e1の中の x (の自由な出現)に、式e3を代入して 得られる式のことであり、以下の例を参照されたい。
インライン化前 let y = ... in let f = fun x -> x + y in let g = fun y -> (f y) * y in g 10 gのインライン化後 let y = ... in let f = fun x -> x + y in let g = fun y -> (f y) * y in (f 10) * 10 fのインライン化後 let y = ... in let f = fun x -> x + y in let g = fun y -> (f y) * y in (10 + y) * 10なお、f を先にインライン化するときには、注意しなければならい。つま り、単純に、
fのインライン化後(誤り) let y = ... in let f = fun x -> x + y in let g = fun y -> (x + y) * y in g 10とやってしまうと誤りになるからで、この場合は、y の変数名を変更して、
fのインライン化後(誤り) let y = ... in let f = fun x -> x + y in let g = fun y1 -> (x + y) * y1 in g 10としなければならない。
インライン化のもう1つの注意として、(当然だが)再帰関数は、インライン化 しても消えないので、単純にやると無限にインライン化してしまうことになる、 ということがある。(従って、通常、再帰関数はインライン化し ない。)
亀山幸義