2015年3月4日水曜日

Christopher StracheyのGPM

12 Days of Christmas

昨年12月12日のブログにクリスマスの歌を書いたのは, GPMのこのマクロを書いた直後であった.

それまでいろいろなプログラム言語で書いてみたので, GPMならどう書けるかと考えるのはまぁ自然だ.

こういう詩のようなものを出力するには, 空白や改行の制御が必要になる. GPMはマクロ評価部分以外は入力をそのまま出力するから簡単かもしれない. 一方, Tarai関数で述べたように, 空白や改行は邪魔なこともあるので, 私のシミュレータは空白改行を無視する/しないの切替え機能を持っている.

またこのマクロは, 本質的なところだけ再現出来ればよいという趣旨で, 元の詩の一部だけを使った.

とはいえ, 難題がある. このGPMの環境では, 通常は0から9までしか数えられないのに, 12日をどう扱うかだ.

とりあえずマクロはこうなっている.
$def,1-,<$-1,0,1,2,3,4,5,6,7,8,9,:,$def,-1,<~>~1;;>;
$def,getn,<$11th,10th,9th,8th,7th,6th,5th,4th,3rd,2nd,1st,
$def,11th,<~>~1;;>;
$def,gets,<$11 Pipers
,10 Lords
,9 Ladies
,8 Maids
,7 Swans
,6 Geese
,5 Rings
,4 Birds
,3 Hens
,2 Turtle Doves
,a Partridge
,$def,11 Pipers
,<~>~1;;>;
$def,song,<$~1,
$def,~1,<On the $getn,>~1<; day
$gets,>~1<;>~2<
$song,$1-,>~1<;,$gets,>~1<;>~2<;>;,
$def,-1,<On the 12th day
12 Drummers
>~2;;>;
$song,:,;
最初のマクロ1-はこれまでの1-とそっくりで, 引数が0なら-1, 1なら0, ... が返る. これは~nを読むとnのアスキーコードから48を引き何番目かを決める. ところでアスキーの表で9の次が:(コロン)なのを利用して:を10として扱うことにした. では;(セミコロン)を11として使えるかというと;はマクロ呼出しの終りを示すからこれは使えない. だが0から9の世界を0から10の世界に拡張できた. それでもまだ12日には問題が残る.

getnは引数が0なら11th, 1なら10th, ..., 10なら1stが返る. 次のgetsは同様で, 0なら11 Pipers改行, 1なら10 Lords改行, ... ,10ならa Partridge改行が返る. カンマが改行の次にあるからだ. 何行か飛ばして最後の行のsongの呼出しを見よう.

$song,:,;とマクロ呼出しになっている. つまり第1引数は:(コロン, 上述のように10), 第2引数は空文字列になっている.

そこでsongの定義をみると, 第1引数のifになっており, -1なら$def,-1,にあるようにOn the 12th day改行12 Drummers改行にして第2引数をつなげる. else部はOn the (getnの返した1st day改行みたいなもの), 続いて$gets, ~1;続いて第2引数. さらにsongを呼ぶようになっている.

という次第で
On the 1st day
a Partridge

On the 2nd day
2 Turtle Doves
a Partridge
...(3rdから11thまで省略)
On the 12th day
12 Drummers
11 Pipers
10 Lords
9 Ladies
8 Maids
7 Swans
6 Geese
5 Rings
4 Birds
3 Hens
2 Turtle Doves
a Partridge
の出力が得られた.

Color RingのRGB

つぎのような虹の両端を繋げた絵が描きたかった. 各色のRGBは円周外に併記してあるようになっている. 赤字がRの値, 緑字がGの値, 青字がBの値である. それぞれの値は0から0xffまでである.



円周外の文字は見難いいので, 変化の様子を図にすると



上の円形の図に右方向の赤が横軸の0で, RGBがff0000, そこから60度の間に緑が00からffへ増える(横軸30の地点). 次は赤が減り, 青が増え, 緑が減り, 赤が増え, 青が減るような変化である.

これは円周360度を36分割してあるが, さらに連続的な絵も描けるわけで, 2度置きに色を変えるとすると

for(i=0; i<180; i++) {
var ang0=-i*Math.PI/90-Math.PI/180;
var ang1=-i*Math.PI/90+Math.PI/180;
context.beginPath();
context.moveTo(r*Math.cos(ang1),r*Math.sin(ang1));
context.lineTo(R*Math.cos(ang1),R*Math.sin(ang1));
context.arc(0,0,R,ang1,ang0,true);
context.lineTo(r*Math.cos(ang0),r*Math.sin(ang0));
context.closePath();
context.fillStyle=cs[i];
context.fill();}
のように扇型のような領域を下にあるcs[i]のRGBで塗り分けることになる.

ここでのマクロはcs[i]の値を生成するものである.
var cs = new Array (
"#ff0000", "#ff0900", "#ff1100", "#ff1a00", "#ff2200", "#ff2b00", 
"#ff3300", "#ff3c00", "#ff4400", "#ff4d00", "#ff5500", "#ff5e00", 
"#ff6600", "#ff6f00", "#ff7700", "#ff8000", "#ff8900", "#ff9100", 
"#ff9a00", "#ffa200", "#ffab00", "#ffb300", "#ffbc00", "#ffc400", 
...
"#ff0033", "#ff002b", "#ff0022", "#ff001a", "#ff0011", "#ff0009",
0);
60度の間に0から255まで変化する. 2度ずつだと255を30で割るから1回の増減量は8.5に相当する. 緑の増え方をみると, 最初が0, 次が9(8.5を四捨五入した), 次が 十六進で11つまり17(8.5*2), ... となっていることが分る.

しかしこの計算はせず, この昇順と降順の値をマクロで定義した.
$def,00^,09;$def,09^,11;$def,11^,1a;$def,1a^,22;$def,22^,2b;
$def,2b^,33;$def,33^,3c;$def,3c^,44;$def,44^,4d;$def,4d^,55;
$def,55^,5e;$def,5e^,66;$def,66^,6f;$def,6f^,77;$def,77^,80;
$def,80^,89;$def,89^,91;$def,91^,9a;$def,9a^,a2;$def,a2^,ab;
$def,ab^,b3;$def,b3^,bc;$def,bc^,c4;$def,c4^,cd;$def,cd^,d5;
$def,d5^,de;$def,de^,e6;$def,e6^,ef;$def,ef^,f7;$def,f7^,ff;
$def,09_,00;$def,11_,09;$def,1a_,11;$def,22_,1a;$def,2b_,22;
$def,33_,2b;$def,3c_,33;$def,44_,3c;$def,4d_,44;$def,55_,4d;
$def,5e_,55;$def,66_,5e;$def,6f_,66;$def,77_,6f;$def,80_,77;
$def,89_,80;$def,91_,89;$def,9a_,91;$def,a2_,9a;$def,ab_,a2;
$def,b3_,ab;$def,bc_,b3;$def,c4_,bc;$def,cd_,c4;$def,d5_,cd;
$def,de_,d5;$def,e6_,de;$def,ef_,e6;$def,f7_,ef;$def,ff_,f7;
Hilbert曲線の時と同様, ^は上向き, _は下向きとし, $def,00^,09;は上向きで00の次は09という定義である.
$def,next,<$~1^;>;
$def,last,<$~1_;>;
と定義すると
$next,00; => 09
$next,$next,00;; => 11
のようになる. 次にfとbを定義する.
$def,f,<$~2, 
 $def,~2,<"#>~1~2~3<",$f,>~1<,$next,>~2<;,>~3<;>;,
 $def,ff,;;>; 
$def,b,<$~2, 
 $def,~2,<"#>~1~2~3<",$b,>~1<,$last,>~2<;,>~3<;>;,
 $def,00,;;>; 
そして
$f,ff,00,00;
と呼ぶと, 第1引数はff, 第2引数は00, 第3引数も00である. fの定義は第2引数=ffなら何もしない; そうでないなら"#の次に3つの引数をつなげ",を出力しfを続けて呼ぶ. 但し第2引数をnextにする となっている. 従って
"#ff0000","#ff0900","#ff1100",...
が得られる. 続いてbを$b,,ff,ff00;と呼ぶと, 第1引数は空文字列, 第2引数はff, 第3引数はff00だから, RGBのRの値を次々と減らした列が得られるのである.

こうして出来た配列で描いたColor Ringがこれだ.

0 件のコメント: