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型には変更はない。

次に、インタープリタに環境を導入する。 改訂版を 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式の処理は少し複雑だが、以下の手順で行われる。

  1. e1 を現在の環境 env のもとで計算する.(eval3 e1 env)
  2. その計算結果を v とすると、x=v を環境 env に追加し、拡張された環境 env1 を得る.
  3. e2 を環境 env1 のもとで評価し、それが Let式の評価の最終結果となる。

課題 3-2.

注. 今回の処理系では、環境は単なるリストであり、 lookup関数は、先頭から順番に1つずつ見ていくので、非常に効率が悪い。 たとえば、もし、let文が 100回ネストしていたら、環境は長さ 100のリストとなり、 最悪の場合リストの要素を100回検査する必要がある。 この効率を改善するためには、どのような方法があるか答えなさい。 (この問題は、方法を答えればよく実装まではしなくてよい。 しかし、時間的余裕がある人は,それを実装すれば、加点要素とする。なお、 本実験の最後の方で扱うコンパイラでは、この問題に1つの解を与えている。)
トップ, 前へ, 次へ.

亀山幸義