2.タートルグラフィクスのコマンドを作る

コマンドとは

タートルグラフィクスでは、亀に、「前に5歩進め」「右に45度向け」などの指示を出して亀を歩かせます。このような指示(命令)を「コマンド」といいます。これをCindyScriptの「関数」として定義することにしましょう。

日本語のLOGOでは「前へ」「右へ」「左へ」のようなコマンドになりますが、CindyScriptは英語が基本なのでこれらのコマンドも英語にしましょう。

ひとまず、次の3つを作ります。

forward(n)  前へnだけ進め nは整数とは限りませんので、nではなくxを使うことも考えられます。

right(θ) 右へθだけ向きを変えろ(θは「シータ」と読みます。ギリシャ文字で、角度を表すときによく使います)

left(θ)  左へθだけ向きを変えろ

 

CindyScriptで関数を作るには、次のようにします。

 

forward(n):=(行うことがら)

 

ここで、単なる等号ではなく、コロンと等号を使います。すると、右辺に書かれた内容が左辺の言葉の定義となります。括弧の中の数は「引数(ひきすう)」といいます。

  では、前へ進むコマンド forwardを作りましょう。ただ前へ進むだけでなく、動いたあとを線分として引きます。また、進んだ先をあらためて原点とします。つまり、座標系を平行移動するのです。関数として定義する(行なうことがら)は、次の2つです。

原点からx軸上の点(n,0)まで線を引く・・・・ draw()関数を使います。

(n,0)まで平行移動する。 ・・・・translate()関数を使います。

 ------------------------------------------------------------------------------------------------------

forward(n):=(

draw([0,0],[n,0]);

translate([n,0]);

);

------------------------------------------------------------------------------------------------------

次に、「右へ向きを変える」と「左へ向きを変える」です。座標系を回転する rotate(θ)  関数を使いますが、θはプラスのときは反時計回りに、マイナスのときには時計回りに回転します。また、「θ」は入力しにくい(漢字入力モードで「しーた」と打って変換)ので、適当なアルファベットの文字(言葉)にします。角をあらわす angle とでもしましょう。

 ------------------------------------------------------------------------------------------------------

left(angle):=rotate(angle);

right(angle):=rotate(-angle);

------------------------------------------------------------------------------------------------------

では、この3つの関数の働きを確かめましょう。亀に正三角形を描かせます。

------------------------------------------------------------------------------------------------------

repeat(3,

    forward(2);

    left(120°);

);

------------------------------------------------------------------------------------------------------

コマンドの記号化と星形五角形

次に、「前へ」「右へ」「左へ」という亀への命令をそれぞれ記号で表すことにします。リンデンマイヤーのL-Systemでは、「前に」を A,B,またはF、「右へ」を − 、「左へ」を+ としていますので、そのようにしましょう。そうしておいて、進む長さと向きを変える角度を決めておくと、これらの記号を使って星形五角形を描くことができます。いままでのものも含めて、次のようにします。

------------------------------------------------------------------------------------------------------

forward(n):=(

draw([0,0],[n,0]);

translate([n,0]);

);

left(angle):=rotate(angle);

right(angle):=rotate(-angle);

turtle(s,n,angle):=(

    if(s=="A",forward(n));

    if(s=="B",forward(n));

    if(s=="F",forward(n));

    if(s=="+",left(angle));

    if(s=="-",right(angle));

);

kakudo=72°;

fig="+F--F--F--F--F";

repeat(length(fig),

    turtle(fig_#,2,kakudo);

);

------------------------------------------------------------------------------------------------------

turtle(s,n,angle) という関数の中で、文字sによって前に進んだり向きを変えたりします。

fig="+F−−F−−F−−F−−F"; が亀に指令する命令文です。(−記号は2つずつです)

まず、+で72°左(反時計回り)に向き、

Fで一歩進み-- で72°右(時計回り)に2回すなわち144°向く というのを繰り返すという命令文です。

次の

repeat(length(fig),

    turtle(fig_#,2,kakudo);

);

では、

length(fig) 回だけ turtle(fig_#,2,kakudo); を実行します。

lengthというのはCindyScriptの組み込み関数で、文字列 fig の文字数を数えます。fig="+F--F--F--F--F"ですから、length(fig)の結果は 14 になります。つまり、14回繰り返すことになります。

 fig_# のアンダーバーは、文字列 fig の#番目の文字を取り出す演算子です。「演算子」というのは「計算」と同じようなものです。#番目の#には、repeatで繰り返すときに、CindyScriptが自動的に1から順に数字を代入していきます。この#は「実行変数」と呼ばれます。1回目は#は1に、繰り返した2回目は#は2に・・・・ となっていきます。

結果として、"+F--F--F--F--F"で表された命令「左へ72°向く」「前へ2進む」「右へ144°向く」「前へ2進む」「右へ144°向く」・・・を順に実行することになるのです。

星形がどの順に描かれたかわかりますか?亀になったつもりで追ってみましょう。

スロットの利用

さて、forwardなどの関数は、一度定義してしまえば、そのあとは変更する必要はないですね。このような、最初に1度だけ実行すればよいものは、Initialization スロットに書き、いろいろ変更する可能性のあるものを Drawスロットに書くようにすると見通しがよくなります。

次のように、スロットに分けて実行してみましょう。

さて、drawスロットに書いた  repeat(length(fig), turtle(fig_#,2,kakudo);); ですが、これも定型的な処理ですから、関数化して、Initilizationスロットに入れてしまいましょう。関数名を tree とします。文字列はs、長さはn、角はangleにしました。

------------------------------------------------------------------------------------------------------

tree(s,n,angle):=(

   gsave();

   repeat(length(s),turtle(s_#,n,angle));

   grestore();

);

------------------------------------------------------------------------------------------------------

ここで、gsave() と grestore() は先ほどありませんでした。gsave()は、現在の画面設定を保存する関数、grestore()は保存した画面設定を戻す関数です。この関数が必要な理由は後述します。

この関数を使って、drawスロットは次のように変更します。

------------------------------------------------------------------------------------------------------

kakudo=72°;

fig="+F--F--F--F--F";

tree(fig,2,kakudo);

------------------------------------------------------------------------------------------------------

ところで、この星形五角形ですが、描き終わったあと亀がどちらを向いているかわかりますか?

亀になったつもりで命令を受けてみると、最後に向きを変えたあと、 F で長さ2の線分を描いて終わっています。そのときの向きは? 線分を描いた方向ですから左下ですね。では、このあと続いて、draw([2,0],color->[1,0,0],size->3); で点を打ってみましょう。亀の向いている方向は常にx軸の正の方向と思っているわけですから、左斜め下に点が打たれるはずですが・・・

x軸上の(2,1)に点が打たれています。亀は最初の状態に戻っています。

実は、これが gsave()とgrestore() の働きなのです。

gsave() はそのときの状態を記憶します。原点の位置、軸の方向などです。

grestore() は記憶した状態に戻します。

では、ほんとうにそうなのか確かめましょう。initializationスロットに書いた、tree()関数の、gsave()とgrestore()の前に // を書いてコメント行にしてみます。コメント行なので 関数としては働きません。実行するとつぎのようになります。

亀は星形五角形を書き終わって、左下を向いたままです。そちらがx軸の正の方向と思っているわけですから、左下に赤い点を打ちました。亀にしてみれば正しい行動をしたことになりますね。

「3.置き換えシステム」へ進む

初めに戻る