22
言言言言言言言 言言 言言言言言 4: 2009 言 11 言 13 言 言言 () 言言 言言

言語処理系入門4

Embed Size (px)

DESCRIPTION

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

Citation preview

Page 1: 言語処理系入門4

言語処理系入門

第4回:関数の実装2009 年 11 月 13 日(金)服部 健太

Page 2: 言語処理系入門4

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

Page 3: 言語処理系入門4

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;

Page 4: 言語処理系入門4

2009/11/13 言語処理系入門 4 4

クロージャ

関数を表すオブジェクト <仮引数名,関数本体のコード,環境>の3つ組み 環境は関数が生成される時点のもの

関数適用時,仮引数を実引数の値で対応させたエントリが環境に追加され,その新しい環境のもとで,関数本体の式が評価される

例 :let y = -9 in

fn x = x + y

x

x + y

変数 値

y -9

Page 5: 言語処理系入門4

2009/11/13 言語処理系入門 4 5

クロージャが不要な場合

C 言語の場合 関数の入れ子定義はできない. 関数ポインタはある⇒ 単なる関数の開始アドレス

Pascal 言語の場合 関数を返す関数は定義できない 関数の入れ子定義はできる⇒ アクセスリンクを辿って,外側の変数にアクセ

ス可能

Page 6: 言語処理系入門4

2009/11/13 言語処理系入門 4 6

形式的意味

以下のように ( 抽象 ) 構文を拡張E ::= E E 関数適用V ::= fn x -> E 関数生成

値v Val = Clos(x,E,ρ)∈

),,(| ExClosEx -fn

vEE

vEvxvEExClosE

21

21

|

'|}''{'|)',','(|

Page 7: 言語処理系入門4

2009/11/13 言語処理系入門 4 7

関数の実装

抽象構文の拡張( syntax.ml )let rec expr = … | AppExpr of expr * exprand 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)) e2and 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'

Page 8: 言語処理系入門4

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]])

カリー化という

Page 9: 言語処理系入門4

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

11nn

nn

ii vxvxvEExEx

vEnivE

inandrec let

Page 10: 言語処理系入門4

2009/11/13 言語処理系入門 4 10

let rec の実装

eval.mllet 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'

仮の値で環境を拡張

後で正式な値で環境を更新

Page 11: 言語処理系入門4

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オペレータが動くことを確認し,なぜ再帰を生み出すのか考えよ

Page 12: 言語処理系入門4

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 中に出現しない)

Page 13: 言語処理系入門4

参照呼び出し( 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 1each for )( if

Page 14: 言語処理系入門4

参照呼び出しのセマンティクス

右辺式が変数ならば,コピーしない

環境の実装 環境に格納する値が,直接的な値( DirectObjec

t )か参照値( IndirectObject )かを区別できるようにする

2009/11/13 言語処理系入門 4 14

)',(|,

)',(|}{,

SvEyxS

SvElxS

inlet

)( ly

Direct Int 5

Indirect

x

y

Page 15: 言語処理系入門4

参照呼び出しの実装

参照はがし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

Page 16: 言語処理系入門4

参照呼び出しの実装(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

Page 17: 言語処理系入門4

参照呼び出しの実装(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

Page 18: 言語処理系入門4

名前呼び出し( Call by Name ) 関数呼び出し時に,引数を評価しない.

関数の中で実際に引数を使うときに評価する 値呼び出しと名前呼び出しとでの重要な動作の

違い# let rec loop x = loop x;# let do_nothing x = 0;# do_nothing (loop 0);???

値呼び出しだと無限ループするが,名前呼び出しだと停止する 実用的な使い途として無限ストリームの実装に使え

2009/11/13 言語処理系入門 4 18

Page 19: 言語処理系入門4

無限ストリームの例

// 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

Page 20: 言語処理系入門4

名前呼び出しの実装

関数適用のとき,引数を評価せず,かわりにサンクオブジェクト(引数なしの関数)を作成し,束縛する.(* サンクオブジェクトを作成する *)and thunk env e = Value.ThunkObj (fun () -> eval_expr env e)

変数を評価するときに,対象がサンクオブジェクトの場合は,サンクオブジェクトを呼び出して結果を得る. サンプルプログラムでは deref 関数内で処理let deref = function… | Value.ThunkObj t -> t()

2009/11/13 言語処理系入門 4 20

Page 21: 言語処理系入門4

演習課題

今週のサンプルプログラムを動かしてみよ サンプルプログラムでは,値呼び出し,名前呼び出し参照呼び

出しを実装してある. 値呼び出しの場合

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

Page 22: 言語処理系入門4

2009/11/13 言語処理系入門 4 22

次回予定

日時: 2009 年 11 月 20 日(金) 17 : 00 - 18 : 30

場所: LB2 3F/A 会議室

内容: いろいろなデータ型の扱い

タグ付きレコード,文字列 etc.