(* 式の型 *) type exp = ... | Var of string | Let of string * exp * exp変数 x は、Var "x" という形のデータとして表現する。 また、let x = e1 in e2 という式は、Let("x",e1,e2)という形のデータとして表現する。 なお、今回は、値(計算結果となる式)の種類は増えていないので、value型には変更はない。
次に、インタープリタに環境を導入する。 改訂版を eval3と呼ぶと、eval3は exp型の式だけでなく、 環境も引数として受けとり、value型の値を返す関数である。つまり、eval3 の型は、
eval3 : exp -> (string * value) list -> valueとなる。ここで、環境の型が (string * value) list である。
さて、eval3 を書いてみよう。 eval3 の引数に環境を表す env が加わるので、 eval3 の中でeval3自身を再帰的に呼び出すところもすべて env を引数に追加する。
(* eval3 : exp -> (string * value) list -> value *) (* let と変数、環境の導入 *) let rec eval3 e env = (* env を引数に追加 *) let binop f e1 e2 env = (* binop の中でも eval3 を呼ぶので env を追加 *) match (eval3 e1 env, eval3 e2 env) with | (IntVal(n1),IntVal(n2)) -> IntVal(f n1 n2) | _ -> failwith "integer value expected" in match e with | Var(x) -> 変数の処理は後で追加する | IntLit(n) -> IntVal(n) | Plus(e1,e2) -> binop (+) e1 e2 env (* env を追加 *) | Times(e1,e2) -> binop ( * ) e1 e2 env (* env を追加 *) | Eq(e1,e2) -> ... | If(e1,e2,e3) -> begin match (eval3 e1 env) with (* env を追加 *) | BoolVal(true) -> eval3 e2 env (* env を追加 *) | BoolVal(false) -> eval3 e3 env (* env を追加 *) | _ -> failwith "wrong value" end | Let(x,e1,e2) -> Let式の処理はこれから追加する | _ -> failwith "unknown expression"変更は、eval3 を呼びだす部分に、env という引数を追加しただけである。 さて、いよいよ Var と Letの処理を記述する。
let rec eval3 e env = ... match e with | Var(x) -> lookup x env | ... | Let(x,e1,e2) -> let env1 = ext env x (eval3 e1 env) in eval3 e2 env1 | _ -> failwith "unknown expression"まず、変数 x の処理だが、環境 env の中の x に対応する値を取ってくればよい。 このために lookup関数を使う。env の中に x が含まれないときは、 x に値が束縛されていないという意味であるのでエラーとなる。
Let式の処理は少し複雑だが、以下の手順で行われる。
(* テスト *) let var_x = Var("x") ;; (* 空環境で評価するとエラーになるはず *) let var_y = Var("y") ;; (* 空環境で評価するとエラーになるはず *) let ex32_1 = Let("x",IntLit(3),Plus(var_x,IntLit(5))) ;; let ex32_2 = Let("x",ex32_1,Times(ex32_1,var_x)) ;; let ex32_3 = Let("x",ex32_1,Let("y",IntLit(5),Times(var_x,Var("y")))) ;; let ex32_4 = Let("x",ex32_1,If(Eq(IntLit 2,var_x),Times(var_x,var_x),IntLit(5))) ;;
(* テスト *) let ex32_5 = Let("x",IntLit(3),Let("x",IntLit(5),var_x)) ;; let ex32_6 = Let("x",IntLit(3),Let("x",BoolLit(true),var_x)) ;; let ex32_7 = Let("x",IntLit(3),Let("x",IntLit(5),Let("x",IntLit(7),var_x))) ;; let ex32_8 = Let("x",IntLit(3),Let("y",Plus(var_x,IntLit(1)),Times(var_x,var_y))) ;; let ex32_9 = Let("x",IntLit(3),Let("x",Plus(var_x,IntLit(1)),Times(var_x,IntLit(2)))) ;;
(* テスト *) let ex32_10 = Let("x",IntLit(3),Let("y",IntLit(5),Plus(var_x,var_y))) ;;
また、let x= 1 in let y = x+1 in x+y のような式を eval3 で評価して、 処理の過程で環境がどのように変化していくか考えなさい。 (eval3 の処理の過程で、環境がどうなっているかを見るために は、「環境を印刷する」関数を用意して、環境の中身が変更されるごとにその 関数を呼びだせばよい。)
(* テスト *) let ex32_20 = Let("x",IntLit(1),Plus(Let("x",IntLit(2),Plus(var_x,IntLit(3))),Times(var_x,IntLit(4)))) ;;
亀山幸義