3-2. 変数と letの処理の続き

変数と letの処理を実装するため、 まず、ミニOCaml言語の式の定義に、 これらを追加する。
(* 式の型 *)
type exp =
    ...
  | Var of string        
  | Let of string * exp * exp 
つまり、 変数 x は、Var "x" という形のデータとして表現する。 また、let x = e1 in e2 という式は、Let("x",e1,e2)という形のデータとし て表現する。 なお、今回は「値」は増えていないので、value型には変更はない。

次に、インタープリタ eval2 に環境を導入する。 改訂版を 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) ->
    (match (eval3 e1 env) with         (* env を追加 *)
      BoolVal(true)  -> eval3 e2 env   (* env を追加 *)
    | BoolVal(false) -> eval3 e3 env   (* env を追加 *)
    | _ -> raise Wrong_Value)
  | Let(x,e1,e2) -> Let式の処理はこれから追加する
  | _ -> failwith "unknown expression"
変更は、eval2 を呼びだす部分に、 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関数を使う.(x が含まれな いときは,x に値がまだはいっていないため,エラーとする.)

Let式の処理は,少し複雑だが,まず,e1 を現在の環境 env のもとで計算す る.これが (eval3 e1 env)の部分である.そして,x が,その計算結果の値 を 持つように環境を拡張し,env1 という新しい環境を得る. e2 を,新しい環境 env1 のもとで評価すると,最終的な結果となる.

課題 7.


トップ, 前へ, 次へ.

亀山幸義