4. Outline
1 Symbol 型と Expr 型 – Julia の抽象構文木
2 マクロシステム
3 メタプログラミング – どう使うか
4 / 38
5. Julia の抽象構文木 (AST)
例えば x + (y + z) という式は Julia
の中の人からは右図のように木構造
(抽象構文木)として見えている
:call は関数呼び出しの意味
+ は中置が認められているというだ
けで普通の関数であることに注意
つまり正確には +(x, +(y, z))
Julia は式をこのように抽象構文木に変
換して、それから式の評価をしている
この構造をそのまま保持することで、
式やプログラムそのものをデータと
して扱える(=同図像性)
プログラムを生成するプログラムも
書ける(=メタプログラミング)
:call
:call:+
:+
:x
:y :z
5 / 38
6. Julia の抽象構文木 (AST) と Symbol 型、 Expr 型
Julia における最も重要な型(データ構造)のうち 2 つが
Symbol と Expr である
Symbol は識別子・変数名を表す型
Expr は Julia そのものの抽象構文木(AST)を表す型
これらの型のオブジェクト(シンボル、AST)は:() や
quote ... end を用いることで作ることができる
1 julia > s = :x
2 :x
3
4 julia > typeof(s)
5 Symbol
6
7 julia > ex = :(x + 1)
8 :(x + 1)
9
10 julia > typeof(ex)
11 Expr
6 / 38
7. Julia の抽象構文木 (AST) と Symbol 型、 Expr 型
Expr は head, args, typ の 3 つの field を持つ
1 head は行う操作
2 args は操作の対象(引数)
3 typ は結果の型(確定していれば)
ほとんどの場合で Any であり、気にする必要はほとんど無い
Base.dump でまとめて表示できる
Base.Meta.show_sexpr を使うと S 式で表示できる
1 julia > dump(ex)
2 Expr
3 head: Symbol call
4 args: Array(Any ,(3 ,))
5 1: Symbol +
6 2: Symbol x
7 3: Int64 1
8 typ: Any
9
10 julia > Base.Meta.show_sexpr(ex)
11 (:call , :+, :x, 1)
7 / 38
8. Julia の抽象構文木 (AST) と Symbol 型、 Expr 型
AST は木構造なので節と葉を持つ
節は Expr 型
葉は Symbol 型の値(変数名)かリテラル(数値型・文字列型)
今回、簡単のため Expr, Symbol, リテラルをすべてまとめて
AST と呼ぶ
1 julia > dump( :( x + ( y + z ) ) )
2 Expr
3 head: Symbol call
4 args: Array(Any ,(3 ,))
5 1: Symbol +
6 2: Symbol x
7 3: Expr
8 head: Symbol call
9 args: Array(Any ,(3 ,))
10 1: Symbol +
11 2: Symbol y
12 3: Symbol z
13 typ: Any
14 typ: Any
8 / 38
9. 変数補間 (interpolation)
quote で AST を作るときに、$ を使うことで変数や式の値を
入れることができる
文字列 (" ") やプロセス (‘ ‘) における補間と同じ
1 julia > y = 42
2 42
3
4 julia > :(x = y)
5 :(x = y)
6
7 julia > :(x = $y)
8 :(x = 42)
9
10 julia > :(x = sin (1.0))
11 :(x = sin (1.0))
12
13 julia > :(x = $(sin (1.0)))
14 :(x = 0.8414709848078965)
9 / 38
11. 式の評価
eval 関数に AST を渡すことで、AST の評価を行える
AST に未定義な変数を含めることができるが、定義する前に
AST を評価するともちろんエラーが出る
1 julia > x
2 ERROR: UndefVarError : x not defined
3
4 julia > ex = :(2 * x)
5 :(2x)
6
7 julia > eval(ex)
8 ERROR: UndefVarError : x not defined
9
10 julia > x = 42
11 42
12
13 julia > eval(ex)
14 84
11 / 38
12. 式の操作
Expr は immutable ではないので、AST を操作することがで
きる
1 julia > ex
2 :(2x)
3
4 julia > x, eval(ex)
5 (42 ,84)
6
7 julia > ex.args [2] = 20;
8
9 julia > ex
10 :(20x)
11
12 julia > x, eval(ex)
13 (42 ,840)
12 / 38
17. 変数捕捉と健全なマクロ
Base.macroexpand を使うとマクロ展開した結果を得ること
ができる
関数なので quote が必要
1 module JT3 # 以 下 全 て の マ ク ロ は こ の モ ジ ュ ー
ル 内 に あ る
2 macro setx_A ()
3 :( x = sin (1.0) )
4 end
5 end
1 julia > macroexpand( :( JT3.@setx_A ) )
2 :(#30#x = JT3.sin (1.0))
1 sin を JT3 で修飾することで、呼び出し元が sin を隠蔽して
いるかどうかを気にしなくてよくなる
JT3.sin は(名前の隠蔽をしていなければ)Base.sin になる
2 #30#x という名前を作ることで、呼び出し元に x があるかど
うかを気にしなくてよくなる
17 / 38
18. 変数捕捉と健全なマクロ
1 macro setx_A ()
2 :( x = sin (1.0) )
3 end
Julia が自動的に x を保護してくれるために、残念ながらこの
マクロを使っても呼び出し元の x に変化はおきない
1 julia > x
2 ERROR: UndefVarError : x not defined
3
4 julia > JT3.@setx_A
5 0.8414709848078965
6
7 julia > x
8 ERROR: UndefVarError : x not defined
呼び出し元に影響をあたえるためには、名前の保護を無効化す
ることで意図的に変数捕捉を起こす必要がある (Base.esc)
18 / 38
19. 意図的な変数捕捉
Symbol や Expr に Base.esc を作用させると、それらに含ま
れる名前は保護されなくなる
@setx_B のようにまとめてエスケープしてもよいし
@setx_C のように個別にエスケープしてもよい
Base.esc が引数にとれるのは Symbol か Expr だけなので:x
と quote が必要
esc(:x) の結果を埋め込むために$() が必要
1 macro setx_B ()
2 esc( :( x = sin (1.0)) )
3 end
4 macro setx_C ()
5 :( $(esc(:x)) = sin (1.0) )
6 end
1 julia > macroexpand (:( JT3.@setx_B ) )
2 :(x = sin (1.0))
3
4 julia > macroexpand (:( JT3.@setx_C ) )
5 :(x = JT3.sin (1.0))
19 / 38
20. 意図的な変数捕捉
このマクロを使うと x の値を変えることができる
1 julia > x
2 ERROR: UndefVarError : x not defined
3
4 julia > JT3.@setx_B
5 0.8414709848078965
6
7 julia > x
8 0.8414709848078965
9
10 julia > x = 0;
11
12 julia > JT3.@setx_C
13 0.8414709848078965
14
15 julia > x
16 0.8414709848078965
20 / 38
21. 潔癖症レベルで健全
マクロ引数に含まれている変数名も全て保護される
つまり呼び出し元とは関係ないものとなる
まず確実に esc が必要
Symbol のまま残したいときはエスケープしなくても良い
ぶっちゃけ迷惑
1 macro setf_A(ex , val)
2 :( $ex = $val )
3 end
1 julia > macroexpand (:( JT3.@setf_A x y) )
2 :(#8#x = JT3.y)
3
4 julia > x, y = 0, 42;
5
6 julia > JT3.@setf_A x y
7 ERROR: UndefVarError : y not defined
21 / 38