Piet(和訳Ver)
原文(Original)
Composition with Red, Yellow and Blue. 1921, Piet Mondrian.
|
初めに
Piet(ピエト)は抽象絵画っぽく見えるように作ったプログラミング言語です。名前の由来は、抽象絵画の先駆者
Piet Mondrian(ピエト・モンドリアン)です。
本当はこの言語をMondrian(モンドリアン)と名付けたかったのですが
こんな
(リンク切れにつきWebArchiveのキャッシュをどうぞ)極平凡な言語があったのでやめました。
難解言語じゃなくなっちゃうし仕方ない。
基本理念
Pietの構成
色
#FFC0C0 明るい赤 |
#FFFFC0 明るい黄 |
#C0FFC0 明るい緑 |
#C0FFFF 明るいシアン |
#C0C0FF 明るい青 |
#FFC0FF 明るいマゼンタ |
#FF0000 赤 |
#FFFF00 黄 |
#00FF00 緑 |
#00FFFF シアン |
#0000FF 青 |
#FF00FF マゼンタ |
#C00000 暗い赤 |
#C0C000 暗い黄 |
#00C000 暗い緑 |
#00C0C0 暗いシアン |
#0000C0 暗い青 |
#C000C0 暗いマゼンタ |
#FFFFFF 白 |
#000000 黒 |
Pietでは右表に示した見分けやすい20色を使用します。
上三段の18色は以下の二通りの方法で循環関係になっています:
- 色相の循環: 赤 -> 黄 -> 緑 -> シアン -> 青 -> マゼンタ -> 赤
- 明度の循環: 明色 -> 普通 -> 暗色 -> 明色
「明色」は「暗色」より一周回って「暗い」と考えます。そして白と黒はこの循環に属しません。注意して下さい。
もし上記20色以外の追加色(例えばオレンジ色とか茶色とか)が入ったら、その処理は各処理系に依存します。
例えば簡単な例を挙げると、追加色が白として解釈される処理系であれば、白と同様に使用することが出来ます。
(逆に黒と同様として扱うこともできます。)
codel
Pietは上で定義された色で出来た絵で表現されます。
ピクセル一つ一つの色がこの言語では重要になるので見やすくするために拡大することがよくあります。
拡大すると「ピクセル」と呼んだ時それが元のサイズでの尺度なのか拡大後のサイズでの尺度なのかが紛らわしくなってしまいます。
そこで拡大されている時、拡大前の1ピクセル分のブロック一つを"codel"と呼ぶことにします。
カラーブロック
Pietの基本単位はカラーブロックになります。カラーブロックは隣接した同じ色の複数のcodelで作られ、
違う色のブロックに当たるまでが一つの塊になります。ただし、対角線上に隣接してるものは一塊にカウントしません。
カラーブロックは様々な形になり場合によっては違う色の"穴"を内包するかもしれませんが、
その穴はブロックにもカウントされず無視されます。
スタック
Pietは記憶領域としてスタックを一つだけ持っています。データは整数型しか持ちませんが、コマンドを利用すれば
この整数をUnicodeの値と解釈して文字を表示することも可能です。
プログラムの実行
DP | CC | 選ばれるCodel |
右 | 左 | 右端の上 |
右 | 右端の下 |
下 | 左 | 下端の右 |
右 | 下端の左 |
左 | 左 | 左端の下 |
右 | 左端の上 |
上 | 左 | 上端の左 |
右 | 上端の右 |
Pietのインタプリタは左上からプログラムの実行を始めます。インタプリタはDirection Pointer (DP)
(向きのポインタ)を持っていてこれは最初は右向きを指しています。DPは上下左右のいずれかを指します。
また、Codel Chooser (CC)(codelを選ぶもの)も持っていてこの初期値は左です。CCは左か右を指します。
DPとCCの指す向きはプログラムの実行中にどんどん変化していきます。
プログラムポインタは実行中以下のルールに従ってカラーブロックを移動していきます。
-
プログラムは今いるカラーブロックの中でDPの向きに一番離れた端っこを探します。(形によってはこの端は一繋がりではないかもしれません。)
-
DP方向に向かってCC側の端をさっき見つけた端の中から見つけます。(プログラムの上に立っていてDPの方向に歩いている感じ。
右表も参考のこと。)
-
そしてそのcodelからDPの方向にある隣のカラーブロックのcodelに移動します。
これをプログラムの終了まで繰り返します。
構文要素
数値
Pietでは白でも黒でもないカラーブロックはそのcodel数(面積)が整数値を表します。負の数は表現できませんが、
命令を組み合わせれば可能です。インタプリタが整数を検知したからといってそれが必ずしもプログラムに影響するとは限りません。
値をプッシュする命令の場合に限りその数値をスタックにプッシュします。(命令の詳細は後で説明する。)
黒ブロックと端について
黒のカラーブロックとプログラムの端っこはプログラムの行き止まりに当たります。インタプリタがこれに差し掛かった時
一旦停止してCCを切り替えます(右なら左、左なら右に)。切り替えてから再度移動を試みます。
CCを切り替えても移動できなかった場合はDPを時計回りに90度回します。このようにして全てのパターンのCCとDPを試していきます。
もし8通り全て試みて移動できなかった場合は移動を諦めプログラム終了となります。
白ブロックについて
白ブロックは"フリーゾーン"となりインタプリタは妨げられること無く通り過ぎます。白のエリアに突入してきた時
インタプリタはこの上をDPの方向に滑るように白以外のカラーブロックにたどり着くまで真っ直ぐ通り過ぎて行きます。
もし白の上を通って黒か端までたどり着いてしまった場合はやはり行き止まりになります(上の項目参照)。
それ以外の普通のカラーブロックに着いた場合はそこから普通に進みます。
白を通り抜けてたどり着いた先の色では命令として解釈はしません(下の項目参照)。
持っている色を命令を実行せずに変更できるのでループ等の実装に最適です。
白ブロックの挙動について(追記:2008.1.25): (この辺り訳がちょっと微妙 by訳者)
インタプリタは白ブロック上を一直線に色の着いたブロックに当たるまで進みます。
先に説明した普通のカラーブロックの時のような動き方はしません。
白ブロックを通りすぎて黒ブロックや端に当たった時正確にどうなるのかが元の仕様では不明確でした。
私の解釈を以下に記しておきました。
-
インタプリタは一直線に"滑って"白ブロックの上を通り過ぎる。
-
もし行き止まりにあたったらCCを切り替えるが、インタプリタの行く先は変わらないのでDPを変える。
-
新しい向きのDPで今いる白codelの上からカラーブロックに到達するか別の行き止まりにぶつかるまで滑る。
-
白ブロックの上で行き止まりにぶつかる度にCCやDPを切り替えて滑りなおしてみる。
これをカラーブロックに到達するまで繰り返す(その場で)。
白ブロックの上から試してダメだった場合はもと来た道を引き返す。
コマンド
| 明度 |
色相 | 変化なし | 1段階変化 | 2段階変化 |
変化なし | | push | pop |
1段階変化 | add | subtract | multiply |
2段階変化 | divide | mod | not |
3段階変化 | greater | pointer | switch |
4段階変化 | duplicate | roll | in(number) |
5段階変化 | in(char) | out(number) | out(char) |
コメンドは今いるカラーブロックから次にインタプリタが移動する先のカラーブロックの色の変化で示されます。
色相と明度の変化量が実行する命令を右表のように決定します。白ブロックを挟んでの命令実行はされません。
それぞれのコマンドの内容を以下に説明します。
-
push: カラーブロックの示す整数値をスタックにプッシュします。ちなみにただカラーブロックがあるだけで
自動的にプッシュされるわけではないので値をプッシュしたい時は必ずこの命令を実行して下さい。
-
pop: スタックから1つポップしてその値を破棄します。
-
add: スタックから2つポップして加算した結果をスタックに積みます。
-
subtract: スタックから2つポップして2つ目にポップされた値から最初にポップした値を引いて結果をスタックに積みます。
-
multiply: スタックから2つポップして乗算した結果をスタックに積みます。
-
divide: スタックから2つポップして2つ目にポップされた値を最初にポップした値で割ってその商をスタックに積みます。
-
mod: スタックから2つポップして2つ目にポップされた値を最初にポップした値で割ってその余りをスタックに積みます。
-
not: スタックからポップした値が1なら0を、0なら0以外をスタックに積みます。
-
greater: スタックから2つポップして2つ目の値が1つ目より大きければ1を、そうでなければ0をプッシュします。
-
pointer: スタックから1つポップしてその値の分だけDPを時計回りに回転させます(負数なら反時計回り)。
-
switch: スタックから1つポップしてその値の分だけCCを切り替えます。
-
duplicate: スタックのトップの値をコピーしてプッシュします。
-
roll: スタックから2つポップして2つ目分の深さまでのスタックの値を一つ目の値の分だけ"回転"させます。
例えば「1つ分深さnだけ回転させる」はトップの値をスタックのn番目に埋め込み、n番目以前の値は
1つずつ上に上がることを意味します。負の数だけ回転させる場合は逆回転になります。深さが負の数になった場合はエラーとなり
コマンドが無視されます。
-
in: その環境の標準入力(STDIN)から数値を受け取りスタックにプッシュします。
-
out: スタックから値をポップしてその環境の標準出力(STDOUT)に渡して出力します。
これらの命令が実行できない場合(例えばポップする程スタックに値が無いとか)は単純に無視されます。
サンプルプログラム及びその他情報等
原文 | DM氏のサイト
| 訳者のサイト
原文の最終更新日: 2008/1/25(金) 02:48:17 PST.
Copyright © 1990-2010, David Morgan-Mar. dmm@dangermouse.net
Copyright © 2012, けんぼー lupin@kembo.org