31
1 Robot言語 末尾再帰インタープリタ 2014/OCT/18 たけおか@AXE twitter: @takeoka www.takeoka.org/~take/

Robot Language and a Tail Recursive Interpreter

  • Upload
    -

  • View
    1.079

  • Download
    3

Embed Size (px)

DESCRIPTION

とても簡単なプログラミング言語Robotのインタープリタを、 末尾再帰インタープリタとして実現した。 その解説。 スタックが非常に少なくても、末尾再帰の無限ループが、永遠に回る。

Citation preview

Page 1: Robot Language and a Tail Recursive Interpreter

1

Robot言語と

末尾再帰インタープリタ

2014/OCT/18

たけおか@AXEtwitter: @takeoka

www.takeoka.org/~take/

Page 2: Robot Language and a Tail Recursive Interpreter

2

Robot言語

Page 3: Robot Language and a Tail Recursive Interpreter

3

Robot言語とは● 再帰的な曲線を描くための言語● 貧弱だが、ピュア関数型言語といえる● タートル・グラフィックスで画を描く● 非常に単純なプログラミング言語● 繰り返し回数を記憶するために、Accが一つだけある● オリジナルのRobot仕様書 & 実装

Litchen Wang:An Interactive programming Language for control of robots, DDJ, Vol.2, Issue 10, pp.60-63 (1977).

● 日本での紹介文書

石田晴久: ロボット言語, bit,Vol10, No.7, pp.35-50(1978))

● たけおかのページ

http://www.takeoka.org/~take/kvm/robj.html

Page 4: Robot Language and a Tail Recursive Interpreter

4

組み込み関数など● +

● 1+Accをインクリメント

● -● 1-Accをデクリメント

● h● Home 画面中央部に移動

● n● North 画面上方を向く

● c● Clear 画面を消去

● r

● Rotate 45度右へ回る

● f● Forward 線を引きながら1step進む

● j● Jump 線を引かずに1step進む

● t(<非ゼロ文>)(<ゼロ文>)● Test Accが0でないなら<非ゼロ文>のみを

● Accが0なら<ゼロ文>のみを実行する

● dF(<定義文>)● defun ユーザ関数Fを<定義文>として定義する

● a● Acc項になってAccの値を示す

● このRobotにはAccが一つだけある

● (0-2^31の値を取る)

● ユーザ関数● 英小文字かつ予約されていない一

文字がユーザ関数名として使える。

● dでdefineする

Page 5: Robot Language and a Tail Recursive Interpreter

5

実行例● ヒルベルト曲線

du(t(-vfxuyfufxv+)(x))

dv(t(-uyfvfxvyfu+)(y))

dx(6r)

dy(2r)

chna-5+2ru

● 無限ループ

db(z)

dz(frb)

Page 6: Robot Language and a Tail Recursive Interpreter

6

実行例● ドラゴン曲線

dx(t(-x6rk+)(f))

dk(t(-x2rk+)(f))

chna-10+x

● シェルピンスキー曲線

dx(t(-xfxufuxfx+)(v))

du(5r)

dv(2r)

dy(6r3r4(fx))

chna-2+y

Page 7: Robot Language and a Tail Recursive Interpreter

7

テイル・リカーシブ・インタープリタ(末尾再帰インタープリタ)

Page 8: Robot Language and a Tail Recursive Interpreter

8

末尾再帰呼出し→ループ 変換(コンパイル時)● 自分自身の再帰呼出しは、容易に、ループに変換可能● 再帰呼び出し recursive call

int

fact_tail_rec(int n, int a)

{

if(n == 1){ return a; }

else

return fact_tail_rec(n-1, a*n);

}● ループ

int

fact_loop(int n, int a)

{

for(;;){

if(n==1) return a;

a= a * n;

n= n-1;

}

}

Page 9: Robot Language and a Tail Recursive Interpreter

9

コンパイラって楽だよね● コンパイル時間が長くても、怒られない

● コンパイル時に、めちゃくちゃメモリを喰っても怒られない● 程度問題はあるが…

● 広域を見れる

● (自己)末尾再帰を見つけるのも、関数のアタマからお尻までゆっくりと見れる

● 実行時間が速ければそれで良し

● 組込みだと、メモリ・フットプリントも重要だが

– コード・サイズ– ワーキング・メモリ(RAM)

Page 10: Robot Language and a Tail Recursive Interpreter

10

インタープリタは大変● インタープリタの性能 = 実行時間

● JIT なんていうものも大事か…● でも、JIT やるなら、実行前に機械語に変換しろよ

– 大方の言語なら可能だ– Javaとか、わざと複雑にしてるよな– 古いx86の自己書き換えコードなんか動かなくてもいい

よ(フツーの場合)

Page 11: Robot Language and a Tail Recursive Interpreter

11

インタープリタは末尾再帰呼び出しを見抜けるのか?

● Tail recursive call は、コンパイラなら、コンパイル中に

見つけて、静的に変換

● インタープリタって、基本、極めて局所しか見ない● 局所しか見ないで、末尾再帰の最適化はできるのか?

Page 12: Robot Language and a Tail Recursive Interpreter

12

末尾再帰インタープリタ● 末尾再帰の最適化を見つけるためのものじゃないよ● インタープリタの構造が、末尾再帰

● Continuation Passing 的なことを、インタープリタ内部で、

インタープリタ自身がやっている

↓● インタープリタが、不要なスタックを消費しない

● テイル・リカーシブ・インタープリタ : インタープリタの実現方法● 実行時に、実行のために明らかに不要な情報を記憶しない。

● 当然のようであるが、素朴な古い実装では、なかなか難しい。

● テイル・リカーシブな実現のインタープリタで有名な言語は、Schemeである。

Page 13: Robot Language and a Tail Recursive Interpreter

13

本インタープリタの実現int

exeq() // 1命令を実行する

{

n= term();

switch((ccc=CT[Pc++])){

case 'f' :

x1=x + n*v*step; y1=y + n*u*step;

drawLine(x,y, x1,y1, 0);

x=x1; y=y1;

break;

case '\0' : /* func end */

{ Context *c;

c=stk_pop(); Pc=c->pc; CT=c->ct; top= c->top; n= c->n-1;

if(Pc== -1){ contAble=false;finishIt(); return; // 評価、全終了}

if(n>0){ /* n回 繰り返し中 */

c->n=n;

stk_push(c);

CT=top; Pc=0; //再度、関数の先頭

return;

}

return;

}

:

Page 14: Robot Language and a Tail Recursive Interpreter

14

本インタープリタの実現

:

default: /* ユーザ定義関数 */

top= defun[ccc-'a'];

/* 末尾再帰 最適化可能な時は、pushしない

ユーザ関数定義の末尾、すなわち、後に実行すべきものが何もない */

if(n!=1 || CT[Pc]!='\0'){

Context c;

c.n=n; c.top=top; c.pc=Pc; c.ct=CT;

stk_push(&c);

}

CT= top; Pc=0;

//printf(" userFunc %c CT=%x ",ccc,CT);

}

}

Page 15: Robot Language and a Tail Recursive Interpreter

15

ここまで● 関係ないけど…

● Lispプログラマ、Prologプログラマ募集中● ルールベースの人工知能 開発中● 機械学習も取り込んだ、ハイブリッドAI● 普通の32bit or 64bitマシンが対象です

Page 16: Robot Language and a Tail Recursive Interpreter

16

おまけ

Page 17: Robot Language and a Tail Recursive Interpreter

17

古い素朴なインタープリタ実現

● 古い素朴なインタープリタ実現の話をしよう。

Page 18: Robot Language and a Tail Recursive Interpreter

18

昔風、素朴なeval

eval(関数)

{

:

関数をどんどん実行する

:

if(関数呼び出しに出会った)

eval(新しい関数);

残りを実行

:

}

Page 19: Robot Language and a Tail Recursive Interpreter

19

古い素朴なインタープリタ実現

● 関数実行の本体(中心部)は 「eval」と いう関数● evalが、eval自身を再帰的に呼び出す実現が素朴に行われている● 新しい関数を実行するたびに、

evalが呼び出される。● そして、前のevalに戻 るための情報を、常に記憶● 実行対象のソースコード が末尾再帰になっていても…

前のevalのための情報が必ず記憶され、まったく最適化(節約)されない

● 模式evalで、eval中からevalを再帰的に呼び出す● このeval呼び出しによっ て、

「残りを実行」のための情報を記憶する

● 呼び出しの度に、記憶している情報はどんどん増える。

Page 20: Robot Language and a Tail Recursive Interpreter

20

テイル・リカーシブなevaleval(関数)

{

eval_loop:

:

関数をどんどん実行する

:

if(関数呼び出しに出会った){

if(新しい関数呼び出しの後に、仕事はある?){

今evalしている情報を、スタックへ退避

}

プログラムカウンタ=新しい関数;

goto eval_loop;

}

※1

残りを実行

:

※2

if(スタック!=空){

スタックから、情報を戻す

goto eval_loop;

}

}

Page 21: Robot Language and a Tail Recursive Interpreter

21

テイル・リカーシブなeval

※2のあたりで、

うまいことeval_loopへgotoできるようにするのがコツ

難しいところかな。

※1の「残りを実行」は

なるべく無くすのが、インタープリタをすっきりさせるためには、

良い

Page 22: Robot Language and a Tail Recursive Interpreter

22

テイル・リカーシブなeval

● evalそのものが大きなループになっている● 新しい関数の実行をしても、evalの呼び出しは深くならない。

● 関数の末尾からの関数呼び出しは、何も記憶する必要がない● テイル・リカーシブ・インタープリタは、何も記憶しない。

● 関数が自分自身を呼び出していない時も、最適化される

● 実行途中の関数を継続(再開)するために必要な情報は、

ソフトウェアで作った スタックに格納する。● 当然に、テイル・リカーシブでない呼び出しは、

普通に記憶を消費する。

Page 23: Robot Language and a Tail Recursive Interpreter

23

テイル・リカーシブなeval

● 関数の末尾からの関数呼び出しは、何も記憶する必要がない。● テイル・リカーシブ・インタープリタは、何も記憶しない。

↓● テイル・リカーシブ・インタープリタは、

局所しか見ない、単純な構成のインタープリタでも、

末尾再帰ループの場合に、無駄な 記憶を消費することがない

● Prologインタープリタや、コンパイルされたコードを実行する仮想マシンのインタープリタ(実行系)は、

テイル・リカーシブ・インタープリタとして実現されているのが、普通である。

Page 24: Robot Language and a Tail Recursive Interpreter

24

Robot言語詳細

Page 25: Robot Language and a Tail Recursive Interpreter

25

Robot言語とは● 再帰的な曲線を描くための言語● タートル・グラフィックスで画を描く● 非常に単純なプログラミング言語● 繰り返し回数を記憶するために、Accが一つだけある● オリジナルのRobot仕様書 & 実装

Litchen Wang:An Interactive programming Language for control of robots, DDJ, Vol.2, Issue 10, pp.60-63 (1977).

● 日本での紹介文書

石田晴久: ロボット言語, bit,Vol10, No.7, pp.35-50(1978))

● たけおかのページ

http://www.takeoka.org/~take/kvm/robj.html

Page 26: Robot Language and a Tail Recursive Interpreter

26

文法<文> ::= <項><関数> | <項>(<文>) | <文><文>● Robotは<文>を実行します。

● <項>はすぐ後のものの繰り返し回数を表わします。

<項> ::= a | <数> | 「何もなし」● 「何もなし」は1を意味します。

● aはAccの内容を意味します。

<数> ::= 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | <数><数>● 0は2^31を意味します。

<関数> ::= 「組み込み関数」 | 「ユーザ関数」● 「組み込み関数」、「ユーザ関数」ともに以下。

Page 27: Robot Language and a Tail Recursive Interpreter

27

組み込み関数● +

● 1+Accをインクリメント

● -● 1-Accをデクリメント

● h● Home 画面中央部に移動

● n● North 画面上方を向く

● c● Clear 画面を消去

● r

● Rotate 45度右へ回る

● f● Forward 線を引きながら1step進む

● j● Jump 線を引かずに1step進む

● t(<非ゼロ文>)(<ゼロ文>)● Test Accが0でないなら<非ゼロ文>のみを

● Accが0なら<ゼロ文>のみを実行する

● dF(<定義文>)● defun ユーザ関数Fを<定義文>として定義する

Page 28: Robot Language and a Tail Recursive Interpreter

28

予約語● a

● Acc 項になってAccの値を示す

● このRobotにはAccが一つだけある

● (0-2^31の値を取る)

● :● コマンドであることをしめす。

● コマンドは本Robot独自の機能である。

● Robotの言語仕様にはない。

Page 29: Robot Language and a Tail Recursive Interpreter

29

ユーザ関数● 英小文字かつ予約されていない一文字がユーザ関数名として使

えます。● dでdefineする

Page 30: Robot Language and a Tail Recursive Interpreter

30

コマンド(独自拡張)● コマンドはKRobot独自の機能である。

● コマンドは実行管理環境(トップレベル)に対する指令である。

● :d● ユーザ関数の全ての定義を表示する

● :n● ユーザ関数をすべて消去する

● :<項>s● f関数で進む距離をドット数で指定する

● コマンドを実行する場合も文を入力した後、 DoItでトップレベルに評価させる。

● 一部のコマンドは、それを評価することによって、PC,スタックなどに影響を与えてしまう。

● よって、一部のコマンド評価後はContはできなくなる。

Page 31: Robot Language and a Tail Recursive Interpreter

31

以上