Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

言語処理系入門€6

1,124 views

Published on

会社でやっていたソフトウェア基礎講座での講義資料

Published in: Software
  • Login to see the comments

  • Be the first to like this

言語処理系入門€6

  1. 1. 言語処理系入門 第6回:制御構造,例外,継続 2009 年 12 月 4 日(金) 服部 健太
  2. 2. これまでの評価器の構造  例: | PrimAppExpr(op,es) -> let vs = List.map (eval_expr env) es in PrimOp.apply op vs  eval_expr が次にどの部分式を評価するかは,構文木の構造によって暗黙的 に決まっていた.  eval_expr env [[a + (f 5)]] の実行後,次に評価器が何をするのかは文脈に依 存 PrimAppExpr + AppExprVarExpr a VarExpr f ValExpr 5 a + (f 5) 2009/12/4 2言語処理系入門 6
  3. 3. 命令型言語に特有の制御文  return, break, continue など  例:関数の本体中に以下の部分式があるとする  fn x a -> … a / (if x == 0 then return 0 else x) …  式の構造を飛び越えて計算を行うには,工夫が必要! PrimAppExpr / IfExprVarExpr a PrimAppExpr == RetnExpr VarExpr xVarExpr ValExpr ValExpr この部分式 を評価した 後,どうす る? 2009/12/4 3言語処理系入門 6
  4. 4. 継続( continuation )とは  残りの計算を表す概念  プログラムの実行のある時点から最終的な答えを 得るまでの計算  例: let x = 2 in let y = 3 + x * (f x) in y * y;  部分式 (f x) に注目したとき,このときの継続 let y = 3 + x * [<(f x) の値 >] in y * y 2009/12/4 4言語処理系入門 6
  5. 5. 継続渡しスタイル( CPS )  継続を明示的に渡す関数のスタイル  ダイレクトスタイル(通常の形式) let rec fact n = if n == 0 then 1 else n * (fact (n – 1));  継続渡しスタイル let rec fact n k = if n == 0 then k 1 else fact (n-1) (fn v -> k (v * n)); fact(n-1) を計算し た後にする計算 (継続) 2009/12/4 5言語処理系入門 6
  6. 6. 末尾呼び出し( tail call )  処理の最後にくるような関数呼び出し  関数呼び出しから戻ってきた後は,単に return するこ とだけ  例: iter は末尾呼び出しになっている let rec iter x = if x == 0 then     () else     begin print x; iter (x – 1)   end;  CPS では,関数呼び出しは常に tail call となる  tail call は goto に変換できる(スタックを消費しない ) 2009/12/4 6言語処理系入門 6
  7. 7. 継続渡し評価器  次に何をするのか ( 継続 ) を評価器の引数として明 示的に与える  継続渡しスタイルの eval_expr (*   * Env.t -> Cont.t -> Syntax.expr -> Value.t *) let rec eval_expr env cont e = match e with …  継続 cont は,部分式 e を評価した結果を受け取り,最終的 な答えを得るまでの残りの計算.  トップレベルから eval_expr を呼び出すときには, (もう 何もすることが無いことを意味する)空の継続を渡してや る 部分式 e を評価し終 えた後に,やるべき 計算 2009/12/4 7言語処理系入門 6
  8. 8. 継続の表現  継続の表現( cont.ml )  Value.t を受け取って Value.t を返す Ocaml の関数として表 現 type t = Value.t -> Value.t  実際には’ a   -> ’  b の多相型関数として定義  空の継続  もらった値をそのまま返す let init v -> v  はじめの一歩 (main.ml) Syntax.Expr e -> print_result (Eval.eval_expr env Cont.init e) 2009/12/4 8言語処理系入門 6
  9. 9. 評価器( eval.ml )の抜粋  値式の場合: | ValExpr v -> cont (eval_value env v)  If 式の場合 | IfExpr(e1,e2,e3) -> eval_expr env (fun v -> if Value.get_bool v then eval_expr env cont e2 else eval_expr env cont e3 ) e1  関数適用の場合 | AppExpr(e1,e2) -> eval_expr env (fun v1 -> eval_expr env (fun v2 -> clos_apply cont v1 v2) e2) e1 2009/12/4 9言語処理系入門 6
  10. 10. 評価器の処理の流れ  ρ = {a -> 5;f->Cls(x,{},[[x+x]]}, k = Id とする E[[a + (f (f 3))]] ρ k => E[[a]] ρ (fn v -> E[[(f (f 3))]] ρ (fn v’->k(v+v’))) => (fn v -> E[[(f (f 3))]] ρ (fn v’->k(v+v’))) ρ(a) => E[[f (f 3)]] ρ (fn v’->k(5+v’))) => E[[f]] ρ (fn c->E[[f 3]]ρ(fn v->AppCls(c,v,(fn v’->k(5+v’))))) =>(fn c->E[[f 3]]ρ(fn v->AppCls(c,v,(fn v’->k(5+v’))))) Cls(x,{},[[x+x]]) => E[[f 3]]ρ(fn v->AppCls(Cls(x,{},[[x+x]]),v,(fn v’->k(5+v’)))) k‘ = (fn v->AppCls(Cls(x,{},[[x+x]]),v,(fn v’->k(5+v’)))) => E[[f]]ρ(fn c->E[[3]]ρ (fn v->AppCls(c,v, k’))) => … => E[[3]]ρ (fn v->AppCls(Cls(x,{},x+x),v,k’)) => … => AppCls(Cls(x,{},x+x),3,k’) => E[[x+x]]{x->3}k’ ρ‘={x->3} => E[[x]]ρ’(fn v->E[[x]]ρ’(fn v’->k’(v+v’))) => E[[x]]ρ’(fn v’->k’(3+v’)) => … =>(fn v->AppCls(Cls(x,{},[[x+x]]),v,(fn v’->k(5+v’)))) (3+3) => AppCls(Cls(x,{},[[x+x]]),6,(fn v’->k(5+v’))) => E[[x+x]]{x->6} (fn v’->k(5+v’)) => …=>k(5+12)=>Id17=>17 2009/12/4 10言語処理系入門 6
  11. 11. 表示意味論 )))((]][[.(]][[]][[ )}{]][[.(]][[ )}{]][[.(]][[ ]][[ )]][[|]][[.(]][[ ]][[ ))((]][[ )(]][[ .{}, 2121 21 21 321 321 initinit κρλρκρ ρλκκρ κρλρ κρ κρκρλρ κρ ρκκρ κκρ λκρ fEfEEE kvxEkvEx vxEvE EEx EEbbE EEE xx cc vv ΕΕΕ ΕΕ ΕΕ Ε ΕΕΕ Ε Ε Ε = =>− = = →= = = ==   fn inlet elsethenif               eval_expr を数学的に 表記したようなもの 2009/12/4 11言語処理系入門 6
  12. 12. 例外処理  例外の捕捉  E ::= try E1 handle x in E2  例外発生  E ::= raise E1  プログラム例: let f x y = if y == 0 then   raise @Divided_zero   else x / y in try f 10 0 handle x in   case x of @Divided_zero -> 0            | * -> raise x 2009/12/4 12言語処理系入門 6
  13. 13. 例外ハンドラの管理  例外ハンドラ管理モジュール( exn.ml )  例外ハンドラは,単なる継続として表現 type t = (Value.t, Value.t) Cont.t  デフォルトのハンドラ定義  Ocaml の例外を投げてトップレベルに戻る let handler:t ref = ref (fun v -> raise (Unhandled_exception_error v))  ハンドラの設定 let set_handler h = handler := h  ハンドラの取得 let get_handler() = !handler 2009/12/4 13言語処理系入門 6
  14. 14. 例外処理の実装 let rec eval_expr env cont e = match e with … | TryExpr(e1,s,e2) -> let old_h = Exn.get_handler() in let new_h exn_v = Exn.set_handler old_h; eval_expr (Env.extend env [s,Value.DirectObj exn_v]) cont e2 in Exn.set_handler new_h; eval_expr env (fun v -> Exn.set_handler old_h; cont v) e1 | RaiseExpr e1 ->     eval_expr env (fun v -> (Exn.get_handler()) v) e1 元のハンドラ をセットし直 す 新しいハンド ラをセットす る 新しい ハンドラ 定義 2009/12/4 14言語処理系入門 6
  15. 15. 一級継続( first class continuation )  プログラマに,継続を直接操作する手段を提供する  letcc 構文  E ::= letcc k in E1| throw E1 with E2  現在の継続( letcc を取り囲んでいる文脈)を k に束縛し , E1 を実行する  例: # 3 + (letcc k in 2 + (throw k with 5) * 1); ==> 8  変数 k には fn x->3+x という継続を表す関数が束縛され る  継続に値を渡すと(現在の継続を捨てて)その継続を実行 する  呼び出し元には戻らない ジャンプする 2009/12/4 15言語処理系入門 6
  16. 16. 一級継続の応用  深い関数呼び出しからの脱出 let prod ls = letcc k in let rec iter result ls_ = case ls_ of @Nil -> result | @Cons x -> if x.car == 0 then throw k with 0 else iter (result * x.car) x.cdr in iter 1 ls 2009/12/4 16言語処理系入門 6
  17. 17. letcc による break, continue の実現  Syntax Sugar として実装できる  while 式 [[while E1 do E2]]⇒   letcc break in    let rec loop x =       if x then begin      letcc continue in [[E2]]; loop [[E1]]     end     in loop [[E1]]  break/continue [[break]] ⇒ throw break with () [[continue]] ⇒ throw continue with () 2009/12/4 17言語処理系入門 6
  18. 18. letcc の実装  表示意味論  素朴な letcc の実装 | LetccExpr(x,e1) -> eval_expr ( Env.extend env [x,Value.DirectObj(Value.Cont cont)] ) cont e1 | ThrowExpr(e1,e2) -> eval_expr env ( fun k -> eval_expr env (fun v -> (Value.get_cont k) v) e2 ) e1 ))(.]][[.(]][[]][[ }{]][[]][[ 2121 vkvEkEEE xEEx λρλρκρ κκρκρ ΕΕΕ ΕΕ = =     withthrow inletcc  2009/12/4 18言語処理系入門 6
  19. 19. 例外を考慮に入れた letcc の実装  try ブロック中で継続により脱出した場合,例外ハンドラが残ってしま う # letcc k in try k 0 handle c in c; ==> 0 # raise 10; ==> 10???  逆に,継続によって try ブロック内に突入した場合,例外ハンドラがセット されない  正しい実装 | LetccExpr(x,e1) ->      let h = Exn.get_handler() in   eval_expr ( Env.extend env [s,Value.DirectObj(Value.Cont               (fun v -> Exn.set_handler h; cont v))] ) cont e1 継続が呼ばれたら ,元のハンドラを セットし直す 2009/12/4 19言語処理系入門 6
  20. 20. 限定継続( delimited continuation )  継続に区切りを付け,複数の継続を関数のよ うに合成できるようにしたもの.  shift k in E  現在の継続を捕捉して E を評価.その後,現在 の継続を捨てて , 一番最近の reset まで戻る  reset E  継続の仕切り直し init))}((.{]][[]][[ κκλρκρ vkkvxEEx ΕΕ = inshift )]][[(]][[ initκρκκρ EE ΕΕ = reset 2009/12/4 20言語処理系入門 6
  21. 21. shift/reset の実行例 # 1 + (reset 3); ==> 4 # 1 + (reset (2 * (shift k in 4))); ==> 5 # 1 + (reset (2 * (shift k in k 4))); ==> 9 # 1 + (reset (2 * (shift k in k (k 4)))); ==> 17 2009/12/4 21言語処理系入門 6
  22. 22. shift/reset による並行プロセスの実装 let ready_queue = Queue.new(); let rec dispatch _ = if !ready_queue.is_empty() then ready_queue.get() () and yield _ = shift k in begin ready_queue.put (fn -> begin k (); dispatch() end); dispatch() end and spawn ?proc = begin ready_queue.put (fn -> begin reset proc; dispatch() end); yield () end; 2009/12/4 22言語処理系入門 6
  23. 23. 演習問題  今週のサンプルプログラムを動かしてみよ  foreach/yield を追加してみよ.  try/handle/raise を一級継続を用いて syntax sugar として実現できるか? 2009/12/4 言語処理系入門 6 23
  24. 24. 次回予定  日時:  2009 年 12 月 11 日(金) 10 : 30 - 12 : 00  場所:  LB2 3F/C1  内容:  型検査,型推論,多相型 2009/12/4 24言語処理系入門 6

×