Transcript G - OCN

二分木操作言語AATT
メタプログラミングの会
2010年12月4日
郵便はみがき
自己紹介
郵便はみがき
• y-hamigaki(はてな) yhamigaki(Twitter)
• ブログ「かそくそうち」
http://d.hatena.ne.jp/y-hamigaki/
• フリーソフト「Dante98 for Windows」
• ライブラリ「Hamigaki C++ Libraries」
2
木
• ループや孤島がなくノード間の辺が1本
根
辺
ノード
葉
二分木
• 子の数が高々2個
二分探索木
• 小さい値を左の部分木に大きい値を右に
2
1
4
3
5
平衡二分探索木
• 木の偏りを自動的に補正
5
2
4
3
2
1
1
4
3
5
赤黒木
• 根と空ノードは黒
• 赤ノードの子は黒ノード
• 根から葉までの黒ノードの数が同じ
2
1
0
4
3
5
赤黒木への挿入
• 赤ノードとして追加
• 赤黒木の制約を満たさない場合は変形
1
2
1
0
右回転
3
0
2
3
パターンマッチなら簡単?
• Togetter「kinaba さんが Cryolite を洗脳してパ
ターンマッチ厨に仕立て上げるリスト」
http://www.cs.kent.ac.uk/people/staff/smk/redblack/Untyped.hs
balance :: RB a -> a -> RB a -> RB a
balance (T R a x b) y (T R c z d) = T R (T B a x b) y (T B c z d)
balance (T R (T R a x b) y c) z d = T R (T B a x b) y (T B c z d)
balance (T R a x (T R b y c)) z d = T R (T B a x b) y (T B c z d)
balance a x (T R b y (T R c z d)) = T R (T B a x b) y (T B c z d)
balance a x (T R (T R b y c) z d) = T R (T B a x b) y (T B c z d)
balance a x b = T B a x b
AAがないと読めません!
ASCII Art Tree Transform(AATT)
• 英小文字→赤ノード、英小文字→黒ノード
• 数字→赤ノードか黒ノード
• 「^」が探索位置、「*」がルート
*
G
∖
/
p
/
n
^
P
∖
/
U
—>
n
g
∖
/
3
3
∖
U
AATTのパターンマッチ
• 上から順にパターンマッチを行う
• 変形後の木に「^」があれば再帰
G
P
∖
/
p
∖
/
U
— >
n
g
∖
/
n
∖
/
3
3
U
^
G
∖
/
U
P
p
— >
p
g
∖
/
3
∖
/
3
/
n
^
U
n
∖
3
AATTのパース1
• 空行でパターンを分離
G
P
∖
/
p
∖
/
U
— >
n
g
∖
/
n
∖
/
3
3
U
^
G
∖
/
U
P
p
— >
p
g
∖
/
3
∖
/
3
/
n
^
U
n
∖
3
AATTのパース2
• 「->」で入力と出力を分離
G
∖
/
p
/
n
^
P
∖
/
U
—>
n
g
∖
/
3
3
∖
U
AATTのパース3
• 「^」からノードを探索して配列に記録
名前
親
左
右
種別
N
P
nil
nil
起点
P
G
N
3
右親
3
P
nil
nil
右子
G
nil
P
U
右親
U
G
nil
nil
右子
G
p
/
n
この順にノードをチェックする
∖
/
^
U
∖
3
AATTの変形
• 表を見比べて違うリンクとカラーを書き換える
名前 親
左
右
種別
名前 親
左
右
種別
N
P
nil
nil
起点
N
P
nil
nil
左子
P
G
N
3
右親
P
nil
N
G
起点
3
P
nil
nil
右子
3
G
nil
nil
左子
G
nil
P
U
右親
G
P
3
U
右子
U
G
nil
nil
右子
U
G
nil
nil
右子
AATT出力例
inline tree_node* balance(tree_node* root, tree_node* node){
tree_node* pN = node;
if ((pN != 0) && (pN->color == RED)){
tree_node* pP = pN->parent;
if ((pP != 0) && (pP->left == pN) && (pP->color == RED)){
tree_node* pG = pP->parent;
if ((pG != 0) && (pG->left == pP) && (pG->color == BLACK)){
tree_node* pU = pG->right;
if ((pU != 0) && (pU->color == RED)){
pG->color = RED;
pP->color = BLACK;
pU->color = BLACK;
return balance(root, pG);
D言語への移植
•
•
•
•
•
WYSIWYG文字列でDソースコードに埋め込み
テンプレート実引数に設定
D言語のソースコードを出力する関数を作成
mixin文でコンパイル時に評価(CTFE)
dmd 2.050 で確認
D版AATTの問題点1
• mixinコード片中で再帰呼び出しが出来ない
{
TreeNode!(T)* pN = node;
// ...
}
{
TreeNode!(T)* pN = node;
if (…) {
if (TreeNode!(T)* pP = pN.parent) {
node = pP;
// ここで自分自身を呼びたい
goto
• 末尾再帰なのでgoto文で解決
loop:
{
TreeNode!(T)* pN = node;
// ...
}
{
TreeNode!(T)* pN = node;
if (…) {
if (TreeNode!(T)* pP = pN.parent) {
node = pP;
goto loop;
goto文の制約
• 同名の変数定義があるとgotoできない
loop:
{
TreeNode!(T)* pN = node;
// ...
}
{
TreeNode!(T)* pN = node;
if (…) {
if (TreeNode!(T)* pP = pN.parent) {
node = pP;
goto loop;
変数名に序数を付ける
• コード生成関数にシーケンスを渡して別名に
loop:
{
TreeNode!(T)* pN0 = node;
// ...
}
{
TreeNode!(T)* pN1 = node;
if (…) {
if (TreeNode!(T)* pP = pN1.parent) {
node = pP;
goto loop;
D版AATTの問題点2
• CTFEで再帰呼び出しが出来ない
char parseTree(
ref AA_TreeNode[] tree, AsciiArtRect aa,
AA_TreeNode.Types type, char from, size_t y, size_t x)
{
// ...
node.parent = parseTree(
tree, aa, AA_TreeNode.Types.RIGHT_PARENT,
c, pos.y, pos.x
);
再帰だけど再帰じゃない
• 再帰の深度毎に別の関数にしてみる
char parseTree(int depth)(
ref AA_TreeNode[] tree, AsciiArtRect aa,
AA_TreeNode.Types type, char from, size_t y, size_t x)
{
// ...
node.parent = parseTree!(depth+1)(
tree, aa, AA_TreeNode.Types.RIGHT_PARENT,
c, pos.y, pos.x
);
番兵
• ノードは英字26文字+数字10文字のみ
• 文法上再帰の深度は35が最大
• 36段目は空ノードしかありえない
char parseTree(int depth: 36)(
ref AA_TreeNode[] tree, AsciiArtRect aa,
AA_TreeNode.Types type, char from, size_t y, size_t x)
{
return AA_TreeNode.NIL;
}
使用例
TreeNode!(T)* balance3a(T)(TreeNode!(T)* root,
TreeNode!(T)* node)
{
return aatt.transform!(T,r"
G
g
/ ∖
/^∖
p
u -> P U
/
/
n
n
^
")(root, node);
}
まとめ
•
•
•
•
•
AAで書いたツリー操作がそのままコードに
C++とD(D2)に対応
Dへのトランスレータさえ書けばCTFEできる
Dは不思議言語
次のバージョンでは動かないかも