SlideShare une entreprise Scribd logo
1  sur  22
言語処理系入門
第4回:関数の実装
2009 年 11 月 13 日(金)
服部 健太
2009/11/13 言語処理系入門 4 2
関数
 我々の言語に関数を追加したい
# let double x = x + x in
double (3+4);
==> 14
# let abs n =
if n >= 0 then n else –n
in
abs (-123)
==> 123
# let square_add a b = a * a + b * b in
square_add 5 2;
==> 29
2009/11/13 言語処理系入門 4 3
第一級市民 (first class object) とし
ての関数も扱いたい
 高階関数
 …関数を返す関数,関数を引数にとる関数,
 例:
let twice f x = f (f x);
let make_adder x =
   let adder y = x + y
in adder;
 匿名関数
 関数をいちいち定義せずにその場で作れる
twice (fn x -> x + x) 5;
(fn f x -> f x) (fn x -> x * x) 3;
2009/11/13 言語処理系入門 4 4
クロージャ
 関数を表すオブジェクト
 <仮引数名,関数本体のコード,環境>の3つ組
み
 環境は関数が生成される時点のもの
 関数適用時,仮引数を実引数の値で対応させ
たエントリが環境に追加され,その新しい環
境のもとで,関数本体の式が評価される
 例 :
let y = -9 in
fn x = x + y
x
x + y
変数 値
y -9
2009/11/13 言語処理系入門 4 5
クロージャが不要な場合
 C 言語の場合
 関数の入れ子定義はできない.
 関数ポインタはある
⇒ 単なる関数の開始アドレス
 Pascal 言語の場合
 関数を返す関数は定義できない
 関数の入れ子定義はできる
⇒ アクセスリンクを辿って,外側の変数にアクセス
可能
2009/11/13 言語処理系入門 4 6
形式的意味
 以下のように ( 抽象 ) 構文を拡張
E ::= E E 関数適用
V ::= fn x -> E 関数生成
 値
v Val = Clos(x,E,ρ)∈
),,(| ρρ ExClosEx ⇓>− -fn
vEE
vEvxvEExClosE
⇓−
⇓−±⇓−⇓−
21
21
|
'|}''{'|)',','(|
ρ
ρρρρ 
2009/11/13 言語処理系入門 4 7
関数の実装
 抽象構文の拡張( syntax.ml )
let rec expr = …
| AppExpr of expr * expr
and value = …
| FunVal of Symbol.t * expr
 評価器の拡張 (eval.ml)
let rec eval_expr env e = match e with …
| AppExpr(e1,e2) -> clos_apply env
(Value.get_clos (eval_expr env e1)) e2
and eval_value env v = match v with …
| FunVal(x,e) -> Value.Clos(env,x,e)
and clos_apply env (env',x,e') e =
let xvs = eval_binds env [x,e] in
eval_expr (Env.extend env' xvs) e'
2009/11/13 言語処理系入門 4 8
Syntax sugar
 複数の引数を持つ関数
[[fn x1 x2 … xn -> E]]⇒
fn x1 -> (fn x2 -> … (fn xn -> [[E]])…)
 let による関数定義
[[let f x1 … xn = E]]⇒
  let f = [[fn x1 … xn -> [[E]] ]]
 関数適用
[[E1 E2 … En]]⇒
(…(([[E1]] [[E2]]) [[E3]]) … [[En]])
カリー化という
2009/11/13 言語処理系入門 4 9
再帰的定義のための let rec 形式
 再帰関数を定義するには,普通に let を使っ
てもダメ
let fact =
fn n -> if n == 0
then 1 else n * fact(n – 1)
in fact 5
 let rec 形式を導入する
 = の右辺式を拡張した環境の下で評価する
},,{'
|
|'1for|'
11
11
nn
nn
ii
vxvx
vEExEx
vEnivE


±=
⇓==−
⇓−≤≤⇓−
ρρ
ρ
ρρ
inandreclet
2009/11/13 言語処理系入門 4 10
let rec の実装
 eval.ml
let rec eval_expr env e = match e with …
| LetRecExpr(ds,e1) ->
let env' = eval_rec_decls env ds
in eval_expr env' e1
…
and eval_rec_decls env ds =
let env' =
Env.extend env
(List.map (fun (x,e) ->
x,Value.UndefinedObj) ds)
in
let xvs = eval_binds env' ds in
Env.updates env' xvs; env'
仮の値で環境
を拡張
後で正式な値
で環境を更新
2009/11/13 言語処理系入門 4 11
let rec を使わない再帰関数定義
 以下のように fix オペレータ(関数)を定義する
let fix f =
(fn x -> f (fn y-> x x y))
(fn x -> f (fn y-> x x y))
 例: factorial 関数の定義
# let factorial =
fix (fn fct n ->
if n == 0 then 1
else n * fct (n – 1));
# factorial 5;
==> 60
 演習問題:
 fix オペレータが動くことを確認し,なぜ再帰を生み出すのか
考えよ
Syntax sugar(2)
 begin ~ end 文の実装
[[begin E1; E2; … ; En end]]⇒
let _ = [[E1]] in let _ = [[E2]]in … in [[En]]
 while 文の実装
[[while E1 do E2]]⇒
let rec loop x =
if x then begin [[E2]]; loop [[E1]]end
in
loop [[E1]]
2009/11/13 言語処理系入門 4 12
loop, x はフレッシュな
変数
( E1,E2 中に出現しな
い)
参照呼び出し( Call-by-
Reference )
 swap 関数を定義したい
# let x = 3 and y = 4;
# swap x y;
 x ==> 4, y ==> 3 となる
 以下のように定義してもうまくいかない
let swap x y =
let temp = x in
begin x := y; y := temp end;
 引数 x と y は関数呼び出し時にコピーされてしまう
2009/11/13 言語処理系入門 4 13
)',(|,
)',(|},,{},,,{
1for),(|,
22110
111
1
SvEExExExS
SvElxlxvlvlS
niSvES
nn
nnnnin
iiii
⇓===−
⇓−±
≤≤⇓−−
inandandlet 

ρ
ρ
ρ niSDOMl ni ≤≤∉ 1eachfor)(if
参照呼び出しのセマンティクス
 右辺式が変数ならば,コピーしない
 環境の実装
 環境に格納する値が,直接的な値
( DirectObject )か参照値( IndirectObject )か
を区別できるようにする
2009/11/13 言語処理系入門 4 14
)',(|,
)',(|}{,
SvEyxS
SvElxS
⇓=−
⇓−±
inletρ
ρ 
)( ly =ρ
Direct Int 5
Indirect
x
y
参照呼び出しの実装
 参照はがし
let deref = function
| Value.DirectObj v -> v
| Value.IndirectObj loc -> (
match Loc.get_val loc with
| Value.DirectObj v -> v
| Value.IndirectObj _ ->
           raise Invalid_reference_error
| Value.UndefinedObj x ->
           raise (Undefined_variable_error x)
)
   | Value.UndefinedObj x ->
      raise (Undefined_variable_error x)
 評価器
let rec eval_expr env e = match e with …
| VarExpr x -> deref (Env.lookup env x)
2009/11/13 言語処理系入門 4 15
参照呼び出しの実装(2)
 参照先設定
let setref loc v =
let loc' =
match Loc.get_val loc with
| Value.IndirectObj loc'' -> loc''
| _ -> loc
in
Loc.set_val loc' (Value.DirectObj v)
 評価器 (eval_expr)
| AsgnExpr(x,e1) ->
let v = eval_expr env e1 in
setref (get_var_loc env x) v; v
2009/11/13 言語処理系入門 4 16
参照呼び出しの実装(3)
 変数束縛の処理
and eval_bind env (x,e) =
x,(match e with
| VarExpr x' ->
let loc = get_var_loc env x' in (
match Loc.get_val loc with
| Value.IndirectObj loc' ->
Value.IndirectObj loc'
| _ -> Value.IndirectObj loc
)
| _ -> Value.DirectObj(eval_expr env e)
)
2009/11/13 言語処理系入門 4 17
名前呼び出し( Call by Name )
 関数呼び出し時に,引数を評価しない.
 関数の中で実際に引数を使うときに評価する
 値呼び出しと名前呼び出しとでの重要な動作の
違い
# let rec loop x = loop x;
# let do_nothing x = 0;
# do_nothing (loop 0);
???
 値呼び出しだと無限ループするが,名前呼び出
しだと停止する
 実用的な使い途として無限ストリームの実装に使える
2009/11/13 言語処理系入門 4 18
無限ストリームの例
// cons の手続き的な実装
let cons ?x ?y = fn m -> if m then x else y;
let car b = b true;
let cdr b = b false;
// 無限ストリームの実装
let generator n = cons n (generator (n + 1));
# let stream = generator 0;
# car stream;
==> 0
# car (cdr stream);
==> 1
# car (cdr (cdr stream));
==> 2
… 以下メモリの続く限り
2009/11/13 言語処理系入門 4 19
名前呼び出しの実装
 関数適用のとき,引数を評価せず,かわりにサ
ンクオブジェクト(引数なしの関数)を作成し
,束縛する.
(* サンクオブジェクトを作成する *)
and thunk env e =
   Value.ThunkObj
   (fun () -> eval_expr env e)
 変数を評価するときに,対象がサンクオブジェ
クトの場合は,サンクオブジェクトを呼び出し
て結果を得る.
 サンプルプログラムでは deref 関数内で処理
let deref = function
…
| Value.ThunkObj t -> t()2009/11/13 言語処理系入門 4 20
演習課題
 今週のサンプルプログラムを動かしてみよ
 サンプルプログラムでは,値呼び出し,名前呼び出し参照呼び
出しを実装してある.
 値呼び出しの場合
 let x = E, let f x = E
 参照呼び出しの場合
 let &x = E, let f &x = E のように変数名の前に & をつける
 名前呼び出しの場合
 let ?x = E, let f ?x = E のように変数名の前に ? をつける
 以下の挙動を確認せよ
 let rec x = x;
 let rec &x = x;
 let rec ?x = x;
 上の結果の違いをそれぞれ説明せよ
2009/11/13 言語処理系入門 4 21
2009/11/13 言語処理系入門 4 22
次回予定
 日時:
 2009 年 11 月 20 日(金) 17 : 00 - 18 : 30
 場所:
 LB2 3F/A 会議室
 内容:
 いろいろなデータ型の扱い
 タグ付きレコード,文字列 etc.

Contenu connexe

Tendances

F#とC#で見る関数志向プログラミング
F#とC#で見る関数志向プログラミングF#とC#で見る関数志向プログラミング
F#とC#で見る関数志向プログラミング
satoshimurakumo
 
ちょっと詳しくJavaScript 第2回【関数と引数】
ちょっと詳しくJavaScript 第2回【関数と引数】ちょっと詳しくJavaScript 第2回【関数と引数】
ちょっと詳しくJavaScript 第2回【関数と引数】
株式会社ランチェスター
 

Tendances (20)

Swift 3 で新しくなったところ - 表面から見えにくいところを中心に紹介 #ISAOcorp
Swift 3 で新しくなったところ - 表面から見えにくいところを中心に紹介 #ISAOcorpSwift 3 で新しくなったところ - 表面から見えにくいところを中心に紹介 #ISAOcorp
Swift 3 で新しくなったところ - 表面から見えにくいところを中心に紹介 #ISAOcorp
 
Swift らしい表現を目指そう #eventdots
Swift らしい表現を目指そう #eventdotsSwift らしい表現を目指そう #eventdots
Swift らしい表現を目指そう #eventdots
 
プロトコル指向 - 夢と現実の狭間 #cswift
プロトコル指向 - 夢と現実の狭間 #cswiftプロトコル指向 - 夢と現実の狭間 #cswift
プロトコル指向 - 夢と現実の狭間 #cswift
 
F#とC#で見る関数志向プログラミング
F#とC#で見る関数志向プログラミングF#とC#で見る関数志向プログラミング
F#とC#で見る関数志向プログラミング
 
C++14 Overview
C++14 OverviewC++14 Overview
C++14 Overview
 
X hago2 shortcoding 20110827
X hago2 shortcoding 20110827X hago2 shortcoding 20110827
X hago2 shortcoding 20110827
 
apg4b 4.05 ポインタ
apg4b 4.05 ポインタapg4b 4.05 ポインタ
apg4b 4.05 ポインタ
 
ちょっと詳しくJavaScript 第2回【関数と引数】
ちょっと詳しくJavaScript 第2回【関数と引数】ちょっと詳しくJavaScript 第2回【関数と引数】
ちょっと詳しくJavaScript 第2回【関数と引数】
 
Swift 3.0 の新機能 - 追加・変更まわりだけ、ざっくり紹介 2 #devsap
Swift 3.0 の新機能 - 追加・変更まわりだけ、ざっくり紹介 2 #devsapSwift 3.0 の新機能 - 追加・変更まわりだけ、ざっくり紹介 2 #devsap
Swift 3.0 の新機能 - 追加・変更まわりだけ、ざっくり紹介 2 #devsap
 
F#のすすめ
F#のすすめF#のすすめ
F#のすすめ
 
スクリプトで文字コード変換
スクリプトで文字コード変換スクリプトで文字コード変換
スクリプトで文字コード変換
 
Mock and patch
Mock and patchMock and patch
Mock and patch
 
プログラムの実行順序
プログラムの実行順序プログラムの実行順序
プログラムの実行順序
 
repマクロ
repマクロrepマクロ
repマクロ
 
ゲーム開発者のための C++11/C++14
ゲーム開発者のための C++11/C++14ゲーム開発者のための C++11/C++14
ゲーム開発者のための C++11/C++14
 
(Ruby使いのための)Scalaで学ぶ関数型プログラミング
(Ruby使いのための)Scalaで学ぶ関数型プログラミング(Ruby使いのための)Scalaで学ぶ関数型プログラミング
(Ruby使いのための)Scalaで学ぶ関数型プログラミング
 
C++11
C++11C++11
C++11
 
JavaScript 勉強会 ― 変数・演算子・文
JavaScript 勉強会 ― 変数・演算子・文JavaScript 勉強会 ― 変数・演算子・文
JavaScript 勉強会 ― 変数・演算子・文
 
Applicative functor
Applicative functorApplicative functor
Applicative functor
 
while文
while文while文
while文
 

Similaire à 言語処理系入門4

すごいHaskell読書会#1 in 大阪
すごいHaskell読書会#1 in 大阪すごいHaskell読書会#1 in 大阪
すごいHaskell読書会#1 in 大阪
yashigani
 
Lisp tutorial for Pythonista : Day 2
Lisp tutorial for Pythonista : Day 2Lisp tutorial for Pythonista : Day 2
Lisp tutorial for Pythonista : Day 2
Ransui Iso
 
ちょっと詳しくJavaScript 特別編【悪霊の神々】
ちょっと詳しくJavaScript 特別編【悪霊の神々】ちょっと詳しくJavaScript 特別編【悪霊の神々】
ちょっと詳しくJavaScript 特別編【悪霊の神々】
株式会社ランチェスター
 
「Lispインタープリター」勉強会 2014.12.04
「Lispインタープリター」勉強会 2014.12.04「Lispインタープリター」勉強会 2014.12.04
「Lispインタープリター」勉強会 2014.12.04
Minoru Chikamune
 

Similaire à 言語処理系入門4 (20)

言語処理系入門3
言語処理系入門3言語処理系入門3
言語処理系入門3
 
Clojure programming-chapter-2
Clojure programming-chapter-2Clojure programming-chapter-2
Clojure programming-chapter-2
 
F#によるFunctional Programming入門
F#によるFunctional Programming入門F#によるFunctional Programming入門
F#によるFunctional Programming入門
 
yieldとreturnの話
yieldとreturnの話yieldとreturnの話
yieldとreturnの話
 
Swift 2.0 で変わったところ「後編」 #cswift
Swift 2.0 で変わったところ「後編」 #cswiftSwift 2.0 で変わったところ「後編」 #cswift
Swift 2.0 で変わったところ「後編」 #cswift
 
JavaScript 講習会 #1
JavaScript 講習会 #1JavaScript 講習会 #1
JavaScript 講習会 #1
 
すごいHaskell読書会#1 in 大阪
すごいHaskell読書会#1 in 大阪すごいHaskell読書会#1 in 大阪
すごいHaskell読書会#1 in 大阪
 
Introduction Xtend
Introduction XtendIntroduction Xtend
Introduction Xtend
 
たのしい関数型
たのしい関数型たのしい関数型
たのしい関数型
 
関数プログラミング入門
関数プログラミング入門関数プログラミング入門
関数プログラミング入門
 
言語処理系入門€10
言語処理系入門€10言語処理系入門€10
言語処理系入門€10
 
Node.jsでつくるNode.js ミニインタープリター&コンパイラー
Node.jsでつくるNode.js ミニインタープリター&コンパイラーNode.jsでつくるNode.js ミニインタープリター&コンパイラー
Node.jsでつくるNode.js ミニインタープリター&コンパイラー
 
Lisp tutorial for Pythonista : Day 2
Lisp tutorial for Pythonista : Day 2Lisp tutorial for Pythonista : Day 2
Lisp tutorial for Pythonista : Day 2
 
pecl-AOPの紹介
pecl-AOPの紹介pecl-AOPの紹介
pecl-AOPの紹介
 
Pythonで始めるDropboxAPI
Pythonで始めるDropboxAPIPythonで始めるDropboxAPI
Pythonで始めるDropboxAPI
 
ちょっと詳しくJavaScript 特別編【悪霊の神々】
ちょっと詳しくJavaScript 特別編【悪霊の神々】ちょっと詳しくJavaScript 特別編【悪霊の神々】
ちょっと詳しくJavaScript 特別編【悪霊の神々】
 
初心者講習会資料(Osaka.R#7)
初心者講習会資料(Osaka.R#7)初心者講習会資料(Osaka.R#7)
初心者講習会資料(Osaka.R#7)
 
「Lispインタープリター」勉強会 2014.12.04
「Lispインタープリター」勉強会 2014.12.04「Lispインタープリター」勉強会 2014.12.04
「Lispインタープリター」勉強会 2014.12.04
 
普通のプログラミング言語R
普通のプログラミング言語R普通のプログラミング言語R
普通のプログラミング言語R
 
Testman
TestmanTestman
Testman
 

Plus de Kenta Hattori

Plus de Kenta Hattori (20)

オブジェクト指向入門2
オブジェクト指向入門2オブジェクト指向入門2
オブジェクト指向入門2
 
オブジェクト指向入門1
オブジェクト指向入門1オブジェクト指向入門1
オブジェクト指向入門1
 
オブジェクト指向入門10
オブジェクト指向入門10オブジェクト指向入門10
オブジェクト指向入門10
 
オブジェクト指向入門9
オブジェクト指向入門9オブジェクト指向入門9
オブジェクト指向入門9
 
オブジェクト指向入門8
オブジェクト指向入門8オブジェクト指向入門8
オブジェクト指向入門8
 
オブジェクト指向入門7
オブジェクト指向入門7オブジェクト指向入門7
オブジェクト指向入門7
 
オブジェクト指向入門6
オブジェクト指向入門6オブジェクト指向入門6
オブジェクト指向入門6
 
オブジェクト指向入門5
オブジェクト指向入門5オブジェクト指向入門5
オブジェクト指向入門5
 
オブジェクト指向入門4
オブジェクト指向入門4オブジェクト指向入門4
オブジェクト指向入門4
 
オブジェクト指向入門3
オブジェクト指向入門3オブジェクト指向入門3
オブジェクト指向入門3
 
ソフトウェア・テスト入門2
ソフトウェア・テスト入門2ソフトウェア・テスト入門2
ソフトウェア・テスト入門2
 
ソフトウェア・テスト入門1
ソフトウェア・テスト入門1ソフトウェア・テスト入門1
ソフトウェア・テスト入門1
 
ソフトウェア・テスト入門8
ソフトウェア・テスト入門8ソフトウェア・テスト入門8
ソフトウェア・テスト入門8
 
ソフトウェア・テスト入門7
ソフトウェア・テスト入門7ソフトウェア・テスト入門7
ソフトウェア・テスト入門7
 
ソフトウェア・テスト入門6
ソフトウェア・テスト入門6ソフトウェア・テスト入門6
ソフトウェア・テスト入門6
 
ソフトウェア・テスト入門5
ソフトウェア・テスト入門5ソフトウェア・テスト入門5
ソフトウェア・テスト入門5
 
ソフトウェア・テスト入門4
ソフトウェア・テスト入門4ソフトウェア・テスト入門4
ソフトウェア・テスト入門4
 
ソフトウェア・テスト入門3
ソフトウェア・テスト入門3ソフトウェア・テスト入門3
ソフトウェア・テスト入門3
 
アルゴリズムとデータ構造15
アルゴリズムとデータ構造15アルゴリズムとデータ構造15
アルゴリズムとデータ構造15
 
アルゴリズムとデータ構造14
アルゴリズムとデータ構造14アルゴリズムとデータ構造14
アルゴリズムとデータ構造14
 

言語処理系入門4

  • 2. 2009/11/13 言語処理系入門 4 2 関数  我々の言語に関数を追加したい # let double x = x + x in double (3+4); ==> 14 # let abs n = if n >= 0 then n else –n in abs (-123) ==> 123 # let square_add a b = a * a + b * b in square_add 5 2; ==> 29
  • 3. 2009/11/13 言語処理系入門 4 3 第一級市民 (first class object) とし ての関数も扱いたい  高階関数  …関数を返す関数,関数を引数にとる関数,  例: let twice f x = f (f x); let make_adder x =    let adder y = x + y in adder;  匿名関数  関数をいちいち定義せずにその場で作れる twice (fn x -> x + x) 5; (fn f x -> f x) (fn x -> x * x) 3;
  • 4. 2009/11/13 言語処理系入門 4 4 クロージャ  関数を表すオブジェクト  <仮引数名,関数本体のコード,環境>の3つ組 み  環境は関数が生成される時点のもの  関数適用時,仮引数を実引数の値で対応させ たエントリが環境に追加され,その新しい環 境のもとで,関数本体の式が評価される  例 : let y = -9 in fn x = x + y x x + y 変数 値 y -9
  • 5. 2009/11/13 言語処理系入門 4 5 クロージャが不要な場合  C 言語の場合  関数の入れ子定義はできない.  関数ポインタはある ⇒ 単なる関数の開始アドレス  Pascal 言語の場合  関数を返す関数は定義できない  関数の入れ子定義はできる ⇒ アクセスリンクを辿って,外側の変数にアクセス 可能
  • 6. 2009/11/13 言語処理系入門 4 6 形式的意味  以下のように ( 抽象 ) 構文を拡張 E ::= E E 関数適用 V ::= fn x -> E 関数生成  値 v Val = Clos(x,E,ρ)∈ ),,(| ρρ ExClosEx ⇓>− -fn vEE vEvxvEExClosE ⇓− ⇓−±⇓−⇓− 21 21 | '|}''{'|)',','(| ρ ρρρρ 
  • 7. 2009/11/13 言語処理系入門 4 7 関数の実装  抽象構文の拡張( syntax.ml ) let rec expr = … | AppExpr of expr * expr and value = … | FunVal of Symbol.t * expr  評価器の拡張 (eval.ml) let rec eval_expr env e = match e with … | AppExpr(e1,e2) -> clos_apply env (Value.get_clos (eval_expr env e1)) e2 and eval_value env v = match v with … | FunVal(x,e) -> Value.Clos(env,x,e) and clos_apply env (env',x,e') e = let xvs = eval_binds env [x,e] in eval_expr (Env.extend env' xvs) e'
  • 8. 2009/11/13 言語処理系入門 4 8 Syntax sugar  複数の引数を持つ関数 [[fn x1 x2 … xn -> E]]⇒ fn x1 -> (fn x2 -> … (fn xn -> [[E]])…)  let による関数定義 [[let f x1 … xn = E]]⇒   let f = [[fn x1 … xn -> [[E]] ]]  関数適用 [[E1 E2 … En]]⇒ (…(([[E1]] [[E2]]) [[E3]]) … [[En]]) カリー化という
  • 9. 2009/11/13 言語処理系入門 4 9 再帰的定義のための let rec 形式  再帰関数を定義するには,普通に let を使っ てもダメ let fact = fn n -> if n == 0 then 1 else n * fact(n – 1) in fact 5  let rec 形式を導入する  = の右辺式を拡張した環境の下で評価する },,{' | |'1for|' 11 11 nn nn ii vxvx vEExEx vEnivE   ±= ⇓==− ⇓−≤≤⇓− ρρ ρ ρρ inandreclet
  • 10. 2009/11/13 言語処理系入門 4 10 let rec の実装  eval.ml let rec eval_expr env e = match e with … | LetRecExpr(ds,e1) -> let env' = eval_rec_decls env ds in eval_expr env' e1 … and eval_rec_decls env ds = let env' = Env.extend env (List.map (fun (x,e) -> x,Value.UndefinedObj) ds) in let xvs = eval_binds env' ds in Env.updates env' xvs; env' 仮の値で環境 を拡張 後で正式な値 で環境を更新
  • 11. 2009/11/13 言語処理系入門 4 11 let rec を使わない再帰関数定義  以下のように fix オペレータ(関数)を定義する let fix f = (fn x -> f (fn y-> x x y)) (fn x -> f (fn y-> x x y))  例: factorial 関数の定義 # let factorial = fix (fn fct n -> if n == 0 then 1 else n * fct (n – 1)); # factorial 5; ==> 60  演習問題:  fix オペレータが動くことを確認し,なぜ再帰を生み出すのか 考えよ
  • 12. Syntax sugar(2)  begin ~ end 文の実装 [[begin E1; E2; … ; En end]]⇒ let _ = [[E1]] in let _ = [[E2]]in … in [[En]]  while 文の実装 [[while E1 do E2]]⇒ let rec loop x = if x then begin [[E2]]; loop [[E1]]end in loop [[E1]] 2009/11/13 言語処理系入門 4 12 loop, x はフレッシュな 変数 ( E1,E2 中に出現しな い)
  • 13. 参照呼び出し( Call-by- Reference )  swap 関数を定義したい # let x = 3 and y = 4; # swap x y;  x ==> 4, y ==> 3 となる  以下のように定義してもうまくいかない let swap x y = let temp = x in begin x := y; y := temp end;  引数 x と y は関数呼び出し時にコピーされてしまう 2009/11/13 言語処理系入門 4 13 )',(|, )',(|},,{},,,{ 1for),(|, 22110 111 1 SvEExExExS SvElxlxvlvlS niSvES nn nnnnin iiii ⇓===− ⇓−± ≤≤⇓−− inandandlet   ρ ρ ρ niSDOMl ni ≤≤∉ 1eachfor)(if
  • 14. 参照呼び出しのセマンティクス  右辺式が変数ならば,コピーしない  環境の実装  環境に格納する値が,直接的な値 ( DirectObject )か参照値( IndirectObject )か を区別できるようにする 2009/11/13 言語処理系入門 4 14 )',(|, )',(|}{, SvEyxS SvElxS ⇓=− ⇓−± inletρ ρ  )( ly =ρ Direct Int 5 Indirect x y
  • 15. 参照呼び出しの実装  参照はがし let deref = function | Value.DirectObj v -> v | Value.IndirectObj loc -> ( match Loc.get_val loc with | Value.DirectObj v -> v | Value.IndirectObj _ ->            raise Invalid_reference_error | Value.UndefinedObj x ->            raise (Undefined_variable_error x) )    | Value.UndefinedObj x ->       raise (Undefined_variable_error x)  評価器 let rec eval_expr env e = match e with … | VarExpr x -> deref (Env.lookup env x) 2009/11/13 言語処理系入門 4 15
  • 16. 参照呼び出しの実装(2)  参照先設定 let setref loc v = let loc' = match Loc.get_val loc with | Value.IndirectObj loc'' -> loc'' | _ -> loc in Loc.set_val loc' (Value.DirectObj v)  評価器 (eval_expr) | AsgnExpr(x,e1) -> let v = eval_expr env e1 in setref (get_var_loc env x) v; v 2009/11/13 言語処理系入門 4 16
  • 17. 参照呼び出しの実装(3)  変数束縛の処理 and eval_bind env (x,e) = x,(match e with | VarExpr x' -> let loc = get_var_loc env x' in ( match Loc.get_val loc with | Value.IndirectObj loc' -> Value.IndirectObj loc' | _ -> Value.IndirectObj loc ) | _ -> Value.DirectObj(eval_expr env e) ) 2009/11/13 言語処理系入門 4 17
  • 18. 名前呼び出し( Call by Name )  関数呼び出し時に,引数を評価しない.  関数の中で実際に引数を使うときに評価する  値呼び出しと名前呼び出しとでの重要な動作の 違い # let rec loop x = loop x; # let do_nothing x = 0; # do_nothing (loop 0); ???  値呼び出しだと無限ループするが,名前呼び出 しだと停止する  実用的な使い途として無限ストリームの実装に使える 2009/11/13 言語処理系入門 4 18
  • 19. 無限ストリームの例 // cons の手続き的な実装 let cons ?x ?y = fn m -> if m then x else y; let car b = b true; let cdr b = b false; // 無限ストリームの実装 let generator n = cons n (generator (n + 1)); # let stream = generator 0; # car stream; ==> 0 # car (cdr stream); ==> 1 # car (cdr (cdr stream)); ==> 2 … 以下メモリの続く限り 2009/11/13 言語処理系入門 4 19
  • 20. 名前呼び出しの実装  関数適用のとき,引数を評価せず,かわりにサ ンクオブジェクト(引数なしの関数)を作成し ,束縛する. (* サンクオブジェクトを作成する *) and thunk env e =    Value.ThunkObj    (fun () -> eval_expr env e)  変数を評価するときに,対象がサンクオブジェ クトの場合は,サンクオブジェクトを呼び出し て結果を得る.  サンプルプログラムでは deref 関数内で処理 let deref = function … | Value.ThunkObj t -> t()2009/11/13 言語処理系入門 4 20
  • 21. 演習課題  今週のサンプルプログラムを動かしてみよ  サンプルプログラムでは,値呼び出し,名前呼び出し参照呼び 出しを実装してある.  値呼び出しの場合  let x = E, let f x = E  参照呼び出しの場合  let &x = E, let f &x = E のように変数名の前に & をつける  名前呼び出しの場合  let ?x = E, let f ?x = E のように変数名の前に ? をつける  以下の挙動を確認せよ  let rec x = x;  let rec &x = x;  let rec ?x = x;  上の結果の違いをそれぞれ説明せよ 2009/11/13 言語処理系入門 4 21
  • 22. 2009/11/13 言語処理系入門 4 22 次回予定  日時:  2009 年 11 月 20 日(金) 17 : 00 - 18 : 30  場所:  LB2 3F/A 会議室  内容:  いろいろなデータ型の扱い  タグ付きレコード,文字列 etc.