tag:blogger.com,1999:blog-40180433112431037082024-02-19T14:58:28.063+09:00How To Become A HackerPaul Graham と Eric S. Raymond に騙され続けた男の奮闘記cametan_001http://www.blogger.com/profile/04882440058326617382noreply@blogger.comBlogger132125tag:blogger.com,1999:blog-4018043311243103708.post-7504914708131613792016-06-06T05:16:00.001+09:002016-06-06T05:16:47.001+09:00Project Euler Problem 21 - 25<div dir="ltr" style="text-align: left;" trbidi="on">
<script src="https://gist.github.com/anonymous/5c6f58e7cc99c13ea7a85abe7c93761f.js"></script>
<br />
</div>
cametan_001http://www.blogger.com/profile/04882440058326617382noreply@blogger.com0tag:blogger.com,1999:blog-4018043311243103708.post-18509875846129555072016-06-04T02:27:00.004+09:002016-06-04T02:27:43.136+09:00えす式<div dir="ltr" style="text-align: left;" trbidi="on">
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi8tXKCtzvFXlNZ86eGk7d3qRWtkpHvqA0p4kTsU3EHemUxteb2RlDrOtWhL208a3isPSv2nrMk8VHsKH4V_D5cony4PBnOF2N-Jk7fn6eOMxXWj15-sNY4Lo4HZorgT_D5J0ASaRvVfKD6/s1600/s.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi8tXKCtzvFXlNZ86eGk7d3qRWtkpHvqA0p4kTsU3EHemUxteb2RlDrOtWhL208a3isPSv2nrMk8VHsKH4V_D5cony4PBnOF2N-Jk7fn6eOMxXWj15-sNY4Lo4HZorgT_D5J0ASaRvVfKD6/s320/s.jpg" width="280" /></a></div>
<br /></div>
cametan_001http://www.blogger.com/profile/04882440058326617382noreply@blogger.com0tag:blogger.com,1999:blog-4018043311243103708.post-71055668484500710752016-05-29T15:25:00.000+09:002016-05-29T15:25:20.684+09:00Project Euler Problem 16 - 20<div dir="ltr" style="text-align: left;" trbidi="on">
<script src="https://gist.github.com/anonymous/7243680ac47cb6d14db33eaeac35bd83.js"></script>
<br /></div>
cametan_001http://www.blogger.com/profile/04882440058326617382noreply@blogger.com0tag:blogger.com,1999:blog-4018043311243103708.post-31788975161826093832016-05-27T01:35:00.002+09:002016-05-27T01:35:43.790+09:00Project Euler Problem 11 - 15<div dir="ltr" style="text-align: left;" trbidi="on">
<script src="https://gist.github.com/anonymous/41cd081e14a497a45c690925180088ef.js"></script>
</div>
cametan_001http://www.blogger.com/profile/04882440058326617382noreply@blogger.com0tag:blogger.com,1999:blog-4018043311243103708.post-54642094695427337182016-05-26T10:24:00.000+09:002016-05-26T10:24:09.924+09:00Project Euler Problem 1 - 10<div dir="ltr" style="text-align: left;" trbidi="on">
<br />
<script src="https://gist.github.com/anonymous/4af521978cc4dedbc9550703e92984b1.js"></script>
</div>
cametan_001http://www.blogger.com/profile/04882440058326617382noreply@blogger.com0tag:blogger.com,1999:blog-4018043311243103708.post-84596863937862037802016-05-24T22:15:00.001+09:002016-05-24T22:15:18.895+09:00ノイマンの自然数の生成の話<div dir="ltr" style="text-align: left;" trbidi="on">
もう一発小ネタです。 <br />
<blockquote class="twitter-tweet" data-lang="en">
<div dir="ltr" lang="ja">
<a href="https://twitter.com/hashtag/%E6%95%B0%E5%AD%A6%E3%81%AE%E5%95%8F%E9%A1%8C?src=hash">#数学の問題</a> <a href="https://twitter.com/hashtag/%E3%83%97%E3%83%AD%E3%82%B0%E3%83%A9%E3%83%9F%E3%83%B3%E3%82%B0?src=hash">#プログラミング</a><br />
ノイマンの方法で非負整数を作る。<br />
0 = {}<br />
1 = {{}}<br />
2 = {{},{{}}}<br />
3 = {{},{{}},{{},{{}}}}<br />
…<br />
あなたの好きなプログラミング言語で作ってみよう!</div>
— 結城浩 (@hyuki) <a href="https://twitter.com/hyuki/status/732541985240678403">May 17, 2016</a></blockquote>
<br />
これがなかなか手こずりました。<br />
いや、アルゴリズムが、って意味じゃなくって計算速度がなかなか上がらんキッツい問題だな、と言う意味です。<br />
<br />
さて、これは定義上は、見かけはビックリするんですが、実は実装的にはPythonのrange関数(あるいはSchemeのSRFI-1のiota)の再実装みたいなモンです。<br />
つまり、入力nに対して何を返すのか、と言うと実は<br />
<blockquote class="tr_bq">
1を入力 -> [0]を返す<br />
2を入力 -> [0, 1]を返す<br />
3を入力 -> [0, 1, 2]を返す</blockquote>
ってのと全く同じ事をしてるんですね。ただ、最初に0を[]と定義してるだけ、なんで仕組みは簡単なんです。<br />
仮に、Schemeでこういう、ちょっとダウングレードなカンジのrangeなりiotaを実装するのは鼻クソでしょう。<br />
<br />
<script src="https://gist.github.com/anonymous/5c6b637e554eab4b612ff341393f5397.js"></script><br />
<br />
<br />
<br />
ところが、です。<br />
これが0を空リスト、と定義したらちと厄介な事になる。<br />
<br />
<script src="https://gist.github.com/anonymous/a730f7bca662b4e7a0e25a22df7c1874.js"></script><br />
<br />
<br />
<br />
末尾再帰で書いてるんですが、問題に書かれてるn = 0〜3付近の数ならイイんですが、一方、nが10、20、30と増えていくと、計算がどんどん時間かかっていって、100辺りになると終わらないんですね〜。<br />
ロジック的にはどっちも同じなのに計算時間が物凄くかかる、ってのは、後者は空リストのconsを生成してるだけなんだけど、それで膨大な、恐らくヒープを消費してニッチもサッチもいかなくなってくる、って事です。要するに巨大なガベージを生成してるに他なりません。<br />
<br />
実は最初は、遅延評価で書けばどうなんだ、とか思って書いてたんですが、これもnが増大するととんでもない事になっていきます。<br />
<br />
<script src="https://gist.github.com/anonymous/1653c415983f9aeddbbc4d106f73f0ea.js"></script><br />
<br />
<br />
<br />
<br />
多分「定義のクールさ」って意味ではこの遅延評価版が一番カッコイイでしょう。見た目も綺麗ですし。マトモに定義通りに返してくれますし。<br />
<br />
<script src="https://gist.github.com/anonymous/ac7a64c0efd21b7c9df4132ea8788bc7.js"></script><br />
<br />
<br />
<br />
<br />
ところがさすがの遅延評価版でもnが100とかなると計算が終わりません。やはりどっかで大量のconsを扱うのが大変になるんですね〜。<br />
<br />
さて、だったらメモ化はどうだ、って話になるんですが。<br />
通常の高階関数のメモ化を利用して・・・ってのはこの場合は上手くないんです。<br />
基本的に、n = 100を計算する時、n = 100の答えがあったらそいつをハッシュから持ってくる、ってのがメモ化の極意なんですが、生憎、フツーに実装したら今回のような「n = 100の状態を計算する為にはn = 99が必要」のケースだと上手く計算を省略してくれないんですよね。<br />
理想的には再帰的にnを探ってくウチに得た値を利用して計算を行う、と言うちょっとした手動的な最適化が施されてないと、結局毎回新たな値を与える度にn=0まで到達してしまう。<br />
今回のノイマンのモデルだと、どっちにせよnは順番に並んでますし、例えばn = 10を何の情報も無い状態で計算した際にn = 0〜10までのハッシュを全部記録して、n=11の計算の際にn=10「だけ」利用すれば11がすぐ得られる、と言う最適化を施しておいた方が良いわけです。<br />
また、n=11まで計算した後で、n=15を計算するとn=12〜14までの情報は無いわけですが、ハッシュを手繰っていって、n=11の値が見つかった際にそこから計算をスタートしてn=12〜14までをハッシュに埋めつつn=15を返す、ってカタチにしておけば、0まで戻る無駄は省けるわけです。<br />
<br />
<script src="https://gist.github.com/anonymous/b053754fad6b9f6b39bddbb1076b91b2.js"></script><br />
<br />
<br />
<br />
<br />
今回は<a href="http://www.chino-js.com/ja/tech/srfi/srfi-69/srfi-69.html#mkh" target="_blank">SRFI-69のハッシュテーブル</a>を利用しています。<br />
ローカル関数のgetはnが与えられた時、nに対応するvalueがハッシュ内にあるかどうか検索します。nが見つからなかった時にはn-1を検索、それでも見つからない場合は・・・と小さいキーを順次検索していって、valueが見つかった時、そのキーを返します。<br />
ローカル関数putは逆に、あるキーからnに向けて計算した数値を逐次ハッシュに記録していって、キーがnと同じになった時にvalueを返して脱出します。<br />
この2つのローカル関数で、ノイマンの定義を最適化するように試みてるんですが・・・・・・。<br />
それでもやっぱn=100とかだと計算時間かかりすぎて、っつーかやっぱどうもヒープ食いつぶして上手い具合にいかないですねぇ。<br />
ハッキリ言って、この問題は、「計算スピードの省略」じゃどうにもならなくって、どっちかっつーとメモリ容量との戦いの模様です。<br />
なかなかconsは手強いですよ。<br />
<script async="" charset="utf-8" src="//platform.twitter.com/widgets.js"></script>
</div>
cametan_001http://www.blogger.com/profile/04882440058326617382noreply@blogger.com0tag:blogger.com,1999:blog-4018043311243103708.post-58681446571118724402016-05-24T04:12:00.000+09:002016-05-24T04:12:26.915+09:00ズンドコキヨシ with Python<div dir="ltr" style="text-align: left;" trbidi="on">
小ネタです。<br />
<blockquote class="twitter-tweet" data-lang="en">
<div dir="ltr" lang="ja">
Javaの講義、試験が「自作関数を作り記述しなさい」って問題だったから<br />
「ズン」「ドコ」のいずれかをランダムで出力し続けて「ズン」「ズン」「ズン」「ズン」「ドコ」の配列が出たら「キ・ヨ・シ!」って出力した後終了って関数作ったら満点で単位貰ってた</div>
— てくも (@kumiromilk) <a href="https://twitter.com/kumiromilk/status/707437861881180160">March 9, 2016</a></blockquote>
<script async="" charset="utf-8" src="//platform.twitter.com/widgets.js"></script>
クソ、面白いネタなんだけど、知らんかった(笑)。<br />
何せ、3月初頭までパソコン壊れてたんで、知る機会が無かったんですよねぇ。残念。<br />
<br />
さて、ちと問題を読み替えてみます。<br />
単純に確率$p=0.5$で出る「ドコ」を成功、「ズン」を失敗として捉えると「ズンズンズンズンドコ」と言う文字列が出現する確率は<br />
<blockquote class="tr_bq">
失敗×失敗×失敗×失敗×成功 = 0.5×0.5×0.5×0.5×0.5 = 3.125%</blockquote>
になります。そしてこの文字列が出現した「後」に「キ・ヨ・シ!」を繋げてやれば良い。<br />
なお、問題の要請としては、例えば一回「失敗」が多い<br />
<br />
<blockquote class="tr_bq">
ズンズンズンズンズンドコ </blockquote>
<br />
の後でも「キ・ヨ・シ!」が出ても良い模様です。<br />
つまり、この場合、文字列が要素「ズン」と「ドコ」の複合体と見た場合、5つ以上の長さの場合「キ・ヨ・シ!」を繋げる、そうじゃない場合はそのまま出力する、と言う問題として読み替える事が出来るのです。<br />
ちなみに、こういう「失敗を何度か続けた後に成功する」と言う確率分布モデルがあって、それを幾何分布と言います。<br />
<br />
<blockquote class="tr_bq">
$\mathrm{Pr}\left( k\right) :=p\,{\left( 1−p\right) }^{k−1}$</blockquote>
そうすると、要素数自体がこの幾何分布に従う確率変数となって、これで一気に文字列を生成する事が可能となります。<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgn6nUAhwb2OFr1uZ_XKWmF5zNs20iqmDJH51duqEFcN2dsysCTr3PZv6cMRuw7HX8we2f-l9fdaGXccyodbFOvnZwBAAR_JBZOSVN-dkIhA3Sc1wkzAZLUmlbcQSl2k-vSsYXXOkLQqyOY/s1600/Screenshot+from+2016-05-24+03%253A45%253A21.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgn6nUAhwb2OFr1uZ_XKWmF5zNs20iqmDJH51duqEFcN2dsysCTr3PZv6cMRuw7HX8we2f-l9fdaGXccyodbFOvnZwBAAR_JBZOSVN-dkIhA3Sc1wkzAZLUmlbcQSl2k-vSsYXXOkLQqyOY/s320/Screenshot+from+2016-05-24+03%253A45%253A21.png" width="320" /></a></div>
上の写真で言うと、Number of Failures until Success(成功するまでの失敗の数)が4以上の時に「キ・ヨ・シ!」を出力してそれより小さい場合は「キ・ヨ・シ!」を出力しなければ題意が満たせます。<br />
さて、このロジックでプログラムを実装しますが、使用する言語はPython(2.7)、あとは幾何分布に従う確率変数を用いる為、<a href="http://www.numpy.org/" target="_blank">Numpy</a>を利用します。<br />
<br />
<script src="https://gist.github.com/anonymous/440d49338cb5a3ece650d918d0cf992d.js"></script><br />
<br />
<br />
一応、マイブームなんで、問題の要件とは若干違いますが、ジェネレータで実装しています(笑)。<br />
次のようにして遊びます。<br />
<script src="https://gist.github.com/anonymous/ab37a5ab0f4d989aabff64c1ad1a3cc0.js"></script>
<br />
<br />
<br />
<br />
<br />
<br />
これがなかなか、目的の文字列が出ないんですよね(笑)。<br />
ちなみに、フツーに実装しようが、今回みたいに幾何分布を利用して実装しようが結果は(理論的には)変わりません。<br />
基本的に「ズンズンズンズンドコ キ・ヨ・シ!」が成立する確率は計算上、これ以上に「ズン」の部分が長い文字列の場合と合わせて、実は6.25%しかなく、反面、「ズン」の連続が4回に満たない文字列の出現確率は93.75%もあります。<br />
つまり、このジェネレータを回しても10回に1回は出ない、って事なんですね。</div>
cametan_001http://www.blogger.com/profile/04882440058326617382noreply@blogger.com0tag:blogger.com,1999:blog-4018043311243103708.post-78895640846117161992015-07-17T15:20:00.002+09:002015-07-17T15:20:36.417+09:00乱数を作ろう<div dir="ltr" style="text-align: left;" trbidi="on">
<h4 class="tr_bq" style="text-align: left;">
最近のマイブーム</h4>
<div style="text-align: left;">
最近、SFCのすーぱーぷよぷよ通Remixにハマってます。今更ながら、ですが(笑)。</div>
<div style="text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<iframe allowfullscreen="" class="YOUTUBE-iframe-video" data-thumbnail-src="https://i.ytimg.com/vi/DjfxnijDeRE/0.jpg" frameborder="0" height="266" src="https://www.youtube.com/embed/DjfxnijDeRE?feature=player_embedded" width="320"></iframe></div>
<div style="text-align: left;">
<br /></div>
<div style="text-align: left;">
さて、これ遊びながら思ったんですが、あまりにもぷよの出現が偏りがあって、</div>
<div style="text-align: left;">
<br /></div>
<div style="text-align: left;">
「あんま良い乱数使ってないんじゃないの?」</div>
<div style="text-align: left;">
<br /></div>
<div style="text-align: left;">
とかぶつくさボヤいてたんですが(笑)。</div>
<div style="text-align: left;">
しかし待てよ、と。どういう風に乱数プログラミングしてたんだろ?<br />
<br />
<blockquote class="tr_bq">
※ もっとも、良い乱数使ったから、と言って、このテの落ち物パズルのゲーム性が上がる、とは限らないんですがね(笑)。</blockquote>
</div>
<div style="text-align: left;">
<br /></div>
<h4 style="text-align: left;">
機械語には乱数は無い</h4>
<div style="text-align: left;">
ここんとこ、色々古いCPU、例えば6502とかZ80の資料を眺めていたんですが。</div>
<div style="text-align: left;">
基本的には(当たり前かもしれないですが)機械語では「擬似乱数列」ってのは用意されてないんですね。</div>
<div style="text-align: left;">
スーファミのCPUは65816と言うApple II GSで使用されていた、あまりメジャーじゃないCPUが使われています。</div>
<div style="text-align: left;">
90年代初頭のスーファミの時代、ゲームプログラマはアセンブリ言語でプログラムする事が多かった模様です。そして当時は(ある程度は用意されてたみたいですが)開発ツールなんてのも今のようにゴージャスじゃなかった模様です。</div>
<div style="text-align: left;">
と言う事は、かなりの確率で、例えばアクションゲームとかシューティングならさておき、RPG等、自作のアセンブリで書いた乱数ライブラリを使ってたりしたんじゃないでしょうか。そうじゃないと16bit CPU程度ではスピードはあんま稼げなかった筈なんですよね。</div>
<div style="text-align: left;">
擬似乱数。果たしてどうやってプログラムするんでしょう。</div>
<div style="text-align: left;">
<br /></div>
<h4 style="text-align: left;">
SICPの乱数</h4>
<div style="text-align: left;">
話は唐突に飛びますが。</div>
<div style="text-align: left;">
<a href="http://sicp.iijlab.net/fulltext/">計算機プログラムの構造と解釈</a>でも<a href="http://sicp.iijlab.net/fulltext/x312.html">中途半端に</a>(笑)、擬似乱数の作り方が載っています。</div>
<div style="text-align: left;">
<br /></div>
<div style="text-align: left;">
<br /></div>
<blockquote>
<a href="https://www.blogger.com/null" name="index1569"></a>「ランダムに選ばれた」の意味は明瞭ではない. おそらく望んでいるのは<tt> rand</tt>の次々の呼び出しは一様分布という統計の性質を持つ数列を生成することである. 適切な数列の発生法はここでは論じない. そうではなく, 手続き<tt>rand-update</tt>があり, ある数<i>x</i><sub>1</sub>から出発し,<br />
<i>x</i><sub>2</sub> = <tt>(rand-update </tt><i>x</i><sub>1</sub><tt>)</tt><i>x</i><sub>3</sub> = <tt>(rand-update </tt><i>x</i><sub>2</sub><tt>)</tt><br />
を作ると, 値の列<i>x</i><sub>1</sub>, <i>x</i><sub>2</sub>, <i>x</i><sub>3</sub>, ... は望みの統計的性質を持つと仮定する.<a href="http://sicp.iijlab.net/fulltext/x312.html#ftnt6" name="ft6"><small><br /></small></a> <tt>rand</tt>をある固定した値<tt>random-init</tt>に初期化される局所状態変数<tt>x</tt>を持った手続きとして実装出来る. <tt>rand</tt>を呼び出す度に<tt>x</tt>の現在値の<tt>rand-update</tt>を計算し, これを乱数として返し, またそれを<tt>x</tt>の新しい値とする.<br />
<pre>(define rand
(let ((x random-init))
(lambda ()
(set! x (rand-update x))
x)))
</pre>
<br />
もちろん同じ乱数の列を単に<tt>rand-update</tt>を直接呼び出すことで, 代入なしに生成することが出来る. しかしプログラムの乱数を使う部分は<tt> rand-update</tt>に引数として渡す<tt>x</tt>の値をしっかり覚えていなければならないことを意味する. </blockquote>
<pre><a href="https://www.blogger.com/null" name="index1570"></a></pre>
<div style="text-align: left;">
そして注釈には次のような記述があります。<br />
<br />
<blockquote class="tr_bq">
<tt style="background-color: white;">rand-update</tt><span style="background-color: white;">を実装する通常のやり方は, </span><i style="background-color: white;">a</i><span style="background-color: white;">, </span><i style="background-color: white;">b</i><span style="background-color: white;">, </span><i style="background-color: white;">m</i><span style="background-color: white;">を適切に選んだ整数とし, </span><i style="background-color: white;">m</i><span style="background-color: white;">を法として</span><i style="background-color: white;">x</i><span style="background-color: white;">を</span><i style="background-color: white;">ax</i><span style="background-color: white;"> + </span><i style="background-color: white;">b</i><span style="background-color: white;">で更新する規則を使うことである. </span></blockquote>
<br />
要するに具体的な例は全く紹介されてないんですね(笑)。<br />
そこで<a href="https://ja.wikipedia.org/wiki/%E7%B7%9A%E5%BD%A2%E5%90%88%E5%90%8C%E6%B3%95">Wikipedia</a>を頼ったコードは次のようなモノです。<br />
<script src="https://gist.github.com/cametan001/e641c963344c13b4fc2d.js"></script>
<br />
<br />
これは線形合同法と言う極めて単純なやり方なんですが。ところが精度がメチャクチャ悪いんですね。<br />
SICPではこの後、円周率πをモンテカルロ・シミュレーションで求めよう、ってトピックになるわけですが。<br />
<br />
<script src="https://gist.github.com/cametan001/f97150c5bae8ccf7cae9.js"></script>
<br />
<br />
ところが、これやってみりゃ分かるんですが、円周率πに届かないんですよ(笑)。<br />
<br />
<script src="https://gist.github.com/cametan001/ec7d7a042386a4d1757f.js"></script><br />
<br />
さては、この結果知ってて「乱数作成」をサラーっと流したんじゃねぇの(笑)。<br />
もう、SICP、こんなんばっかだよ(苦笑)。<br />
<br />
<h4 style="text-align: left;">
Schemeと乱数</h4>
ところで。<br />
実は<a href="http://milkpot.sakura.ne.jp/scheme/r7rs.pdf">Scheme</a>って仕様には乱数が含まれてないんですね。<br />
まあ、もちろん、各実装には実装依存で乱数生成プログラムが入ってるんでしょうが、あくまで「仕様としては」乱数は持ってません。<br />
そう言う意味では結構メンドくせぇプログラミング言語なんですよね(笑)。<br />
<br />
<h4 style="text-align: left;">
最強の乱数生成法</h4>
<a href="http://www.okada.jp.org/RWiki/">統計処理言語R</a>やお馴染みの<a href="http://www.python.jp/">Python</a>では<a href="https://ja.wikipedia.org/wiki/%E3%83%A1%E3%83%AB%E3%82%BB%E3%83%B3%E3%83%8C%E3%83%BB%E3%83%84%E3%82%A4%E3%82%B9%E3%82%BF">メルセンヌ・ツイスタ</a>と言われる乱数生成プログラムが組み込まれています。メルセンヌ・ツイスタは今のトコ、史上最強と言われる擬似乱数生成プログラムで、仕様がない言語で最強の乱数列が使い放題なんですよねぇ。<br />
一方Lispはどうか、と言うと例えば、Common Lispなんかには<a href="http://www.cliki.net/MT19937">メルセンヌ・ツイスタのライブラリ</a>が公開されていて、共有されています。<br />
Schemeは・・・・・・まあ、実装次第なんでしょうねぇ。<a href="http://practical-scheme.net/gauche/index-j.html">Gauche</a>なんかはメルセンヌ・ツイスタを使ってたと思うんですが、他の実装だと保証の限りじゃないです。まあ、こう言う時、「ライブラリを共有しづらい」Schemeだとやりにくいですね(R7RSでだいぶ仕様上は改善されていますが)。<br />
さて、<a href="http://racket-lang.org/">Racket</a>はどうでしょうか?<br />
<br />
<h4 style="text-align: left;">
Racketの乱数</h4>
残念ながらRacketの乱数はメルセンヌ・ツイスタではございません(笑)。Racketの<a href="http://docs.racket-lang.org/reference/generic-numbers.html?q=random%20number#%28part._.Random_.Numbers%29">Documentation</a>によると、<span style="background-color: white; font-family: Charter, serif; font-size: 17.7000007629395px; line-height: 24.7800006866455px;"><a href="http://marcomaggi.github.io/docs/vicare-libs.html/random-mrg32k3a.html">L’Ecuyer’s MRG32k3a algorithm</a></span>と言うアルゴリズムを用いてるそうです。<br />
何だそりゃ。初めて聞きました。<br />
実は<a href="http://simul.iro.umontreal.ca/rng/MRG32k3a.c">Cのソースコード</a>が公開されていました。<br />
<h4 style="text-align: left;">
</h4>
<script src="https://gist.github.com/cametan001/9eebf5dc9bb43dc2687c.js"></script><br />
これって結構シンプルじゃね?<br />
さぁ、やっと本題です(笑)。<br />
今回はこのMRG32k3aと言うアルゴリズムをRacketで「遅延評価を使って」実装してみたいと思います。<a href="http://sicp.iijlab.net/fulltext/x355.html">SICPの遅延評価の乱数ストリーム</a>の仇をここで取ります(笑)。<br />
<br />
<h4 style="text-align: left;">
遅延評価の友</h4>
ところで。<br />
Schemeでは仕様上、遅延評価に絡んだ機能はdelayとforceしか提供していません。<br />
これだけだと甚だメンド臭く、「マトモに遅延評価をさせる」には、色々基本的な遅延評価用の手続きを実装しないといけません。事実、<a href="http://sicp.iijlab.net/fulltext/x351.html">SICPの遅延評価の項目だとそこに結構な量が割かれている</a>んですよね。<br />
それじゃああんまりにもメンドくせえだろ、って言う貴方に朗報です(笑)。<br />
実は分かりづらいんですが、<a href="http://srfi.schemers.org/srfi-41/srfi-41.html">SRFI-41</a>ってのがあって、これを呼び出せば遅延評価の基本機構は使い放題、となります。<br />
主なトコではstream-cons、stream-car、stream-cdr、stream-null、stream-map、stream-filter、stream-constant、stream-ref、stream-from等等等、SICP程度のお題ならこの程度で大体書けるようになってるライブラリです。<br />
便利でこの辺考えなくって良いんで、これを使いましょう。<br />
<br />
<blockquote class="tr_bq">
※ 何故SRFI-41が「分かりづらい」かと言うと、遅延評価、つまりlazy-evaluationとして題名が付いてなくって「stream」と言う分かりづらい用語になってるからに他ならない。これは歴史的要因で、今ではlazy-evaluationと言う呼び方の方が妥当になってるが、過去にはそのままstreamと呼ばれてた事に由来する。</blockquote>
<br />
<h4 style="text-align: left;">
実装方針</h4>
最終的には単一のプロシージャにまとめますが、途中までは色々バラバラのパーツとして組み立てて行こうと思います。じゃないと遅延評価って良くわかりませんからね。<br />
<br />
<h4 style="text-align: left;">
大域変数</h4>
<br />
まずは大域変数をCコードに則って定義していきます。まあ、最終的にはこれらはletで局所変数化されるんですが、最初はまあ、Cのコードそのままで書いていきます。<br />
<h4 style="text-align: left;">
</h4>
<div>
<script src="https://gist.github.com/cametan001/6e711e8fb8d2bbc7e604.js"></script><br /></div>
<div>
この辺はまあ、いいですよね。乱数の種、SEEDも12345と言うフザけた(笑)値が与えられています。<br />
ちなみに、実はm1、m2と言う2つの値は2の32乗から下2つの素数の模様です。<br />
<br />
<h4 style="text-align: left;">
Component</h4>
さて、Cソースのコメントで、取り敢えずComponent 1と書かれたところに着目してみます。<br />
<br /></div>
<div>
<script src="https://gist.github.com/cametan001/324f8655a17e53f9f10a.js"></script><br />
<br />
さすがC、Lisp慣れしてると物凄くゴチャゴチャして見えますが(笑)。<br />
原因の一つってのはここの箇所って実は「2つの事柄が1つにまとまってる」からなんですよね。<br />
<br />
ここで行われてるのは次の2つです。<br />
<br />
<br />
<ol style="text-align: left;">
<li>p1 なる値の計算</li>
<li>s10、s11、s12と言う3つの数の更新</li>
</ol>
<div>
<br /></div>
<div>
当然Lisperとしては(笑)、「2つの計算目的は2つのプロシージャで書くべきだ」と言う原則を守ろうと思います。</div>
<div>
さて、では手始めに2番から行きますか。</div>
<div>
実はさっき定義しなかったんですが、原版ではs10、s11、s12と言う3つの値も大域変数として定義されています。</div>
<div>
しかし「遅延評価を持ったLisp」であるSchemeではこれはやるべきじゃないトコなんですよね。</div>
<div>
つまり、「各値を更新する」んじゃなくって、s10、s11、s12、そしてその後にはs13、s14・・・って「続いていくだろう」無限ストリームがある、って考えた方がスマートなわけです。</div>
<div>
実際は、元のCコードを見ると、</div>
<div>
<br /></div>
<blockquote class="tr_bq">
(SEED SEED SEED p1 ....)</blockquote>
<div>
<br /></div>
<div>
と言うようなカタチの無限ストリームを想定出来そうだ、って事ですよね。そしてp1以降は適当なプロシージャ、具体的には1の「計算によって」得られた数値がハマっていく。</div>
<div>
そう言う想定から言うとまずは次のような「ストリーム生成」プロシージャをでっち上げられます。</div>
<div>
<script src="https://gist.github.com/cametan001/0352e3d6510ae34e8fec.js"></script><br /></div>
<div>
<br /></div>
<br />
<br />
そしてp1を計算するComponent1なるプロシージャを設計すれば良い。オリジナルのCコードだと次の部分ですね。<br />
<script src="https://gist.github.com/cametan001/304e98a45561caae69a6.js"></script><br />
<br />
<br />
ちなみに、最初、この2行目と3行目で何やってんだかサッパリ分かんなかったんですけど、何とこれ<b>剰余計算</b>やってるみたいです(笑)。え"え"え"え"え"、とか思ったんですが(苦笑)。うげぇ(笑)。<br />
何で%使わねぇのかな。不思議です。ま、いいや(苦笑)。<br />
つまり、この部分をSchemeで書くとこうなる、って事ですね。<br />
<br />
<script src="https://gist.github.com/cametan001/f24b8093bad6fa9b986d.js"></script><br />
<br />
<br />
stream1とComponent1を組み合わせると、例えば実行結果は次のようになります。<br />
<script src="https://gist.github.com/cametan001/bbabd1f930b6c8676c6d.js"></script><br />
<br />
<br />
stream1の最初と2番目と3番目の値はSEEDの値、4番目、5番目の値はそれぞれ1番目と2番目、2番目と3番目の値を使って計算してるから同じ値、6番目からガラッと変わってきますね。<br />
これはstream->listと言うプロシージャでアタマ10個分だけ「実体化」させたわけですが、これがずーっと「無限ストリーム」として続いていくわけです。<br />
<br />
<h4 style="text-align: left;">
Component2</h4>
基本ロジックはComponent1と全く同じなんでサクッと書いていきます。<br />
<script src="https://gist.github.com/cametan001/5285b2d444a5eae8dd53.js"></script><br />
<br />
<br />
ただしComponent1とは<b>使ってる引数の値と引数の順番が若干違う</b>んで引っかからないように(笑)。<br />
<br />
<h4 style="text-align: left;">
Combination</h4>
現時点ストリームが2つあります。p1、p2はそれぞれのストリームの4番目からスタートして、条件に従ってどっちかを選択してちょこちょこと計算して新しいストリームに組み込まれるわけです。<br />
この辺をstream-mapを使って上手いこと実装します。<br />
<script src="https://gist.github.com/cametan001/3a956a3f69f90bd3f44f.js"></script><br />
<br />
Combinationは新しいストリーム、ここでは乱数列を返します。<br />
ちょっとまた、アタマ10個くらいstream->listで実体化させて見てみます。<br />
<br />
<script src="https://gist.github.com/cametan001/4def3586d61bdedf2c58.js"></script><br />
<br />
おお、上手く動いてるようですね。<br />
例えば乱数列の100番目、1000番目、10000番目はそれぞれ<br />
<br />
<script src="https://gist.github.com/cametan001/ae1aab1c73643a984551.js"></script><br />
<br />
となります。<br />
おお、面白い(笑)。これで(基本的に)実装は完了です。<br />
<br />
<h4 style="text-align: left;">
ソースコード</h4>
最後に各パーツを局所手続き化して全コードをまとめます。<br />
<br />
<script src="https://gist.github.com/cametan001/9cf3a1da81f7c0ecb8ca.js"></script><br />
<br /></div>
</div>
</div>
cametan_001http://www.blogger.com/profile/04882440058326617382noreply@blogger.com0tag:blogger.com,1999:blog-4018043311243103708.post-44105836170786276692015-02-26T07:25:00.000+09:002015-02-26T07:25:21.306+09:00言語処理系とは: 補題<div dir="ltr" style="text-align: left;" trbidi="on">
うーん、昨日やってた簡単なインタプリタ/コンパイラ作成ですが、気になってた再帰部分を末尾再帰に書き換えられるのか、ってやってみたんですが、上手く行かなかったですね。<br />
継続受け渡しで書き換えると形式的には末尾再帰になるんですが、最適化はされなさそうです。<br />
あと、コンパイラのコードもちょっとムダな部分があったんで、より関数プログラミングっぽく書きなおしてみました。<br />
<br />
<script src="https://gist.github.com/anonymous/16618f0ec3030236b375.js"></script><br />
<br />
<script src="https://gist.github.com/anonymous/bc8146edf662662005f9.js"></script><br />
<br />
さて、lexと格闘、です。</div>
cametan_001http://www.blogger.com/profile/04882440058326617382noreply@blogger.com0tag:blogger.com,1999:blog-4018043311243103708.post-65937611691239802342015-02-25T14:56:00.000+09:002015-02-25T17:15:00.363+09:00言語処理系とは<div dir="ltr" style="text-align: left;" trbidi="on">
さて、ここ数日、ハードウェアの動作を勉強しようと思って、<a href="http://www.hpcs.cs.tsukuba.ac.jp/~msato/lecture-note/comp-lecture/note1.html#src">このページ</a>を参考にしててちょこちょこプログラムをSchemeで書く為に格闘してました。<br />
いやぁ、なかなかC言語を読むのが難しくて手こずってたんですが、ある程度カタチになったんで、メモ代わりに。<br />
<br />
しっかし、Cのプログラム見てると、大域変数使いまくりで、破壊的変更ありーの、ポインタなんて出てきた日にゃあ何やってんだか一発で分からんし、ホント困ったもんですよ。<br />
例によって、関数プログラミング的に解題していきたいと思います。<br />
<br />
<h2 style="text-align: left;">
インタプリタとコンパイラ</h2>
<blockquote class="tr_bq">
<span style="background-color: #eeeeee;">言語処理系とは、プログラミング言語で記述されたプログラムを計算機上で実 行するためのソフトウエアである。そのための構成として、大別して2つの構 成方法がある。</span><br />
<ul>
<li><em>インタープリター(interpreter,翻訳系)</em>: 言語の意味を解析しながら、その意味する動作を実行する。<blockquote>
<table bgcolor="#CCFFCC" border="0" cellpadding="5"><tbody>
<tr><td><img src="http://www.hpcs.cs.tsukuba.ac.jp/~msato/lecture-note/comp-lecture/fig1/fig1-1.gif" /></td></tr>
</tbody></table>
</blockquote>
</li>
<li><em>コンパイラ(compiler,通訳系)</em>: 言語を他の言語に変換し、その言語の プログラムを計算機上で実行させるもの。狭い意味でコンパイラは、言語を機 械語に変換し、実行するものであるが、他の言語、あるいは仮想機械コードに 変換するものもコンパイラと呼ぶ。他の言語に変換するときには、特に translatorと呼ぶ場合もある。<blockquote>
<table bgcolor="#CCFFCC" border="0" cellpadding="5"><tbody>
<tr><td><img src="http://www.hpcs.cs.tsukuba.ac.jp/~msato/lecture-note/comp-lecture/fig1/fig1-2.gif" /></td></tr>
</tbody></table>
</blockquote>
</li>
</ul>
元のプログラムを<em>ソースプログラム</em>、 翻訳の結果と得られるプログラムを<em>オブジェクトプログラム</em>と呼ぶ。 機械語で直接、計算機上で実行できるプログラム を<em>実行プログラム</em>と呼ぶ。オブジェクトプログラムがアセンブリプログラムの 場合には、アセンブラにより機械語に翻訳されて、実行プログラムを得る。他 の言語の場合には、オブジェクトプログラムの言語のコンパイラでコンパイル することにより、実行プログラムが得られる。仮想マシンコードの場合には、 オブジェクトコードはその仮想マシンにより、インタプリトされて実行される。</blockquote>
<br />
はい、左様でやんすね。<br />
<br />
<h2 style="text-align: left;">
言語処理系の基本構成</h2>
<blockquote class="tr_bq">
コンパイラにしてもインタプリターにしても、その構成は多くの共通部分を持 つ。すなわち、ソースプログラムの言語の意味を解釈する部分は共通である。 インタプリターは、解釈した意味の動作をその場で実行するのに対し、コンパ イラではその意味の動作を行うコードを出力する。<br />
言語処理系は、大きく分けて、次のような部分からなる。<br />
<ol>
<li>字句解析(lexical analysis): 文字列を言語の要素(トークン、token)の列に分解する。</li>
<li>構文解析(syntax analysis): token列を意味を反映した構造に変換。こ の構造は、しばしば、木構造で表現されるので、抽象構文木(abstract syntax tree)と呼ばれる。ここまでの言語を認識する部分を言語のparserと 呼ぶ。</li>
<li>意味解析(semantics analysis): 構文木の意味を解析する。インタプリ ターでは、ここで意味を解析し、それに対応した動作を行う。コンパイラでは、 この段階で内部的なコード、中間コードに変換する。</li>
<li>最適化(code optimization): 中間コードを変形して、効率のよいプログ ラムに変換する。</li>
<li>コード生成(code generation): 内部コードをオブジェクトプログラムの 言語に変換し、出力する。例えば、ここで、中間コードよりターゲットの計算 機のアセンブリ言語に変換する。</li>
</ol>
<blockquote>
<table bgcolor="#CCFFCC" border="0" cellpadding="5"><tbody>
<tr><td><img src="http://www.hpcs.cs.tsukuba.ac.jp/~msato/lecture-note/comp-lecture/fig1/fig1-3.gif" /></td></tr>
</tbody></table>
</blockquote>
コンパイラの性能とは、如何に効率のよいオブジェクトコードを出力できるか であり、最適化でどのような変換ができるかによる。インタープリタでは、プ ログラムを実行するたびに、字句解析、構文解析を行うために、実行速度はコ ンパイラの方が高速である。もちろん、機械語に翻訳するコンパイラの場合に は直接機械語で実行されるために高速であるが、コンパイラでは中間コードで やるべき操作の全体を解析することができるため、高速化が可能である。<br />
また、中間言語として、都合のよい中間コードを用いると、いろいろな言語か ら中間言語への変換プログラムを作ることで、それぞれの言語に対応したコン パイラを作ることができる。<br />
<blockquote>
<table bgcolor="#CCFFCC" border="0" cellpadding="5"><tbody>
<tr><td><img src="http://www.hpcs.cs.tsukuba.ac.jp/~msato/lecture-note/comp-lecture/fig1/fig1-4.gif" /></td></tr>
</tbody></table>
</blockquote>
</blockquote>
<br />
まず、元々はその、字句解析と構文解析をどうやるんだ、ってのの疑問からスタートしたわけですが。<br />
まあ、続けてやっていきましょうか。<br />
<br />
<h2 style="text-align: left;">
例題: 式の評価</h2>
<blockquote class="tr_bq">
さて、例として最も簡単な数式の評価について、インタプリターとコンパイラ を作ってみることにする。目的は,<br />
<blockquote>
<pre>12 + 3 - 4</pre>
</blockquote>
<span style="background-color: #eeeeee;">の式の入力に対し、この式を計算し、</span><br />
<blockquote>
<pre>11</pre>
</blockquote>
<span style="background-color: #eeeeee;">と出力するプログラムを作ることである。これは、式という「プログラミング 言語」を処理する言語処理系である。「式」という言語では、tokenとして、 数字と"+"や"-"といった演算子がある。</span><br />
まずは、字句解析ではこれらのトークンを認識する。例えば、上の例では、<br />
<blockquote>
12の数字、+の演算子、3の数字、-の演算子、4の数字、終わり</blockquote>
<span style="background-color: #eeeeee;">という列に変換する。</span><br />
tokenは、tokenの種類と12の数字という場合の12の値の2つの組で表される。 以下にtokenの種類を定義する<a href="http://www.hpcs.cs.tsukuba.ac.jp/~msato/lecture-note/comp-lecture/ex1/exprParser.h">exprParser.h</a>を示す。</blockquote>
とまあ、ここでCで書かれたソースが示されるんですが、色々やってみた結果、こういうヘッダファイルで定義される <code>#define</code> マクロなんかはSchemeじゃ要らねぇな、ってのが分かりました。最初はCコードに則ってやってこう、って思ったんですが、どうも具合が良くないんでオミットです。何せ、Lisp系言語だとシンボルがそのまま使えるんでこういう「Cっぽい」定義は要らないですね。<br />
<br />
<blockquote class="tr_bq">
<span style="background-color: #eeeeee;">字句解析を行う関数<a href="http://www.hpcs.cs.tsukuba.ac.jp/~msato/lecture-note/comp-lecture/ex1/getToken.c">getToken</a>を示す。</span></blockquote>
さあて、これがまず最初凄くツマッてたトコなんですよね~。まず、<code>ungetc</code>って何だ?とか思って(笑)。<br />
良く分からんC言語の関数で、<a href="http://simd.jugem.jp/?eid=65">調べてみると次のような事が書いて</a>ある。<br />
<br />
<blockquote class="tr_bq">
<span style="background-color: white; font-family: Verdana, 'ヒラギノ角ゴ Pro W3', 'MS ゴシック', Osaka‐等幅; line-height: 18px; margin: 0px; padding: 0px;">読</span><span style="background-color: white; font-family: Verdana, 'ヒラギノ角ゴ Pro W3', 'MS ゴシック', Osaka‐等幅; font-size: 12px; line-height: 18px;">み込んだ文字を1文字押し戻すとは、どういう事なんだ?というわけですが、</span><br />
<br style="background-color: white; font-family: Verdana, 'ヒラギノ角ゴ Pro W3', 'MS ゴシック', Osaka‐等幅; font-size: 12px; line-height: 18px; margin: 0px; padding: 0px;" />
<span style="background-color: white; font-family: Verdana, 'ヒラギノ角ゴ Pro W3', 'MS ゴシック', Osaka‐等幅; font-size: 12px; line-height: 18px;">戻すには </span><strong style="background-color: white; font-family: Verdana, 'ヒラギノ角ゴ Pro W3', 'MS ゴシック', Osaka‐等幅; font-size: 12px; line-height: 18px; margin: 0px; padding: 0px;">ungetc</strong><span style="background-color: white; font-family: Verdana, 'ヒラギノ角ゴ Pro W3', 'MS ゴシック', Osaka‐等幅; font-size: 12px; line-height: 18px;">()を使用します。</span><br />
<br style="background-color: white; font-family: Verdana, 'ヒラギノ角ゴ Pro W3', 'MS ゴシック', Osaka‐等幅; font-size: 12px; line-height: 18px; margin: 0px; padding: 0px;" />
<span style="background-color: white; font-family: Verdana, 'ヒラギノ角ゴ Pro W3', 'MS ゴシック', Osaka‐等幅; font-size: 12px; line-height: 18px;">読み込んだ文字とは、、ファイル読み込みや、キーボードからの入力などの</span><br />
<span style="background-color: white; color: red; font-family: Verdana, 'ヒラギノ角ゴ Pro W3', 'MS ゴシック', Osaka‐等幅; font-size: 12px; line-height: 18px; margin: 0px; padding: 0px;">ストリーム</span><span style="background-color: white; font-family: Verdana, 'ヒラギノ角ゴ Pro W3', 'MS ゴシック', Osaka‐等幅; font-size: 12px; line-height: 18px;">と呼ばれるものから読み込んだ文字です。</span><br />
<span style="background-color: white; font-family: Verdana, 'ヒラギノ角ゴ Pro W3', 'MS ゴシック', Osaka‐等幅; font-size: 12px; line-height: 18px;">その文字を、</span><strong style="background-color: white; font-family: Verdana, 'ヒラギノ角ゴ Pro W3', 'MS ゴシック', Osaka‐等幅; font-size: 12px; line-height: 18px; margin: 0px; padding: 0px;">fgetc</strong><span style="background-color: white; font-family: Verdana, 'ヒラギノ角ゴ Pro W3', 'MS ゴシック', Osaka‐等幅; font-size: 12px; line-height: 18px;">()や、</span><strong style="background-color: white; font-family: Verdana, 'ヒラギノ角ゴ Pro W3', 'MS ゴシック', Osaka‐等幅; font-size: 12px; line-height: 18px; margin: 0px; padding: 0px;">getchar</strong><span style="background-color: white; font-family: Verdana, 'ヒラギノ角ゴ Pro W3', 'MS ゴシック', Osaka‐等幅; font-size: 12px; line-height: 18px;">()などで読み込んだ後、</span><br />
<span style="background-color: white; font-family: Verdana, 'ヒラギノ角ゴ Pro W3', 'MS ゴシック', Osaka‐等幅; font-size: 12px; line-height: 18px;">またもう1度ストリームに戻してしまう、というのが </span><strong style="background-color: white; font-family: Verdana, 'ヒラギノ角ゴ Pro W3', 'MS ゴシック', Osaka‐等幅; font-size: 12px; line-height: 18px; margin: 0px; padding: 0px;">ungetc</strong><span style="background-color: white; font-family: Verdana, 'ヒラギノ角ゴ Pro W3', 'MS ゴシック', Osaka‐等幅; font-size: 12px; line-height: 18px;">()の処理です。</span></blockquote>
うそぉん、ストリームから取ってきた文字をまたストリームに戻せるんかい、とか思って(笑)。さすがC言語(苦笑)。<br />
あっれぇ、Schemeには<code>read-char</code>に対して<code>unread-char</code>なんてあっただろうか、と困ってたわけですね(笑)。当然無いですがね(苦笑)。<br />
そんな時に<a href="http://compassoftime.blogspot.jp/">Sagittarius Schemeの作者の人</a>から助け舟が。<br />
<br />
<blockquote class="twitter-tweet" data-cards="hidden" data-conversation="none" data-partner="tweetdeck">
<a href="https://twitter.com/cametan_001">@cametan_001</a> peek-charで足りないんですか?<br />
— Kei (@tk_riple) <a href="https://twitter.com/tk_riple/status/569919011946950656">February 23, 2015</a></blockquote>
<script async="" charset="utf-8" src="//platform.twitter.com/widgets.js"></script><br />
あ、そう、こういう時<code>peek-char</code>を使うんだ、って今回初めて知った次第です(笑)。ダメじゃん(笑)。<br />
<a href="https://docs.google.com/viewer?url=http%3A%2F%2Fmilkpot.sakura.ne.jp%2Fscheme%2Fr7rs.pdf">仕様書</a>見ても良く分かんなかったもんな~。<br />
<blockquote class="tr_bq">
<pre>[[手続き]] (peek-char)
[[手続き]] (peek-char port)
</pre>
入力ポートportから取り出すことができる次の文字を返すが、次の文字を指し示す様なポートの更新は行なわれない(ポート内の位置は変わらない)。取り出す文字が存在しなかった場合はファイル終りオブジェクトが返される。port引数は省略でき、その場合のデフォルトは<code>current-input-port</code>が返す値である。
<br />
注:
<code>peek-char</code>の呼び出しで返される値は、同じポートに対する<code>read-char</code>の呼び出しで返される値と同じものである。唯一の違いは、ポートに対する直後の<code>read-char</code>呼び出しもしくは<code>peek-char</code>呼び出しで、直前の<code>peek-char</code>呼び出しで返された値が返されるという点である。特に対話型ポートに対して<code>peek-char</code>を呼び出した場合、入力を待ち続けて<code>read-char</code>がハングする時には、<code>peek-char</code>も必ずハングすることになる。
<br />
<pre></pre>
</blockquote>
わりぃ、ホンマ、何言ってるんだかサッパリ分からん(苦笑)。<br />
まあ、要するにこういう事らしいです。ストリームに例えば、"1 2 3 4 5"ってあった場合、<code>read-char</code>は"1"を取ってきたあとストリームを"2 3 4 5"に更新するけど、<code>peek-char</code>の場合は"1 2 3 4 5"から"1"を取ってもストリームを"1 2 3 4 5"のままに置いておくそうです。<br />
まあ、敢えて言うと、こういう入出力系ってインタプリタで試しづらいんですよね。<code>read-char</code>だとまだいいんですが、<code>peek-char</code>だと無限ループみたいな事になって、何だか良く分からん事になります。<br />
<br />
<script src="https://gist.github.com/anonymous/7e0fad3f30b0b3e0f603.js"></script><br />
インタプリタ上のReaderで<code>'hoge</code>と入力すると、ストリームに<code>'hoge</code>が入って、次から<code>read-char</code>を呼び出すと、ストリームに残ってた文字が一字づつ出力されていきます。<br />
一方、<code>peek-char</code>の場合、ポートが更新されないんで永遠に一文字目の<code>#\'</code>がずーっと出力され続けますね。これを解除するにはもう一度<code>read-char</code>を呼んでポートを更新しないといけません。<br />
<br />
これさえ分かれば<a href="http://www.hpcs.cs.tsukuba.ac.jp/~msato/lecture-note/comp-lecture/ex1/getToken.c">Cのソース</a>をSchemeで書きなおすのは簡単です。<br />
<br />
<script src="https://gist.github.com/anonymous/f88c1cca9a460c5a2582.js"></script><br />
Scheme版では、<a href="http://www.hpcs.cs.tsukuba.ac.jp/~msato/lecture-note/comp-lecture/ex1/exprParser.h">exprParser.h</a>で大域変数として定義されてた<code>tokenVal</code>と<code>currentToken</code>の2つを<code>getToken</code>プロシージャ内部から多値で返すようにしています。わざわざ大域変数として定義して破壊的に変更するのも嫌ですしね。<br />
<br />
<blockquote class="tr_bq">
<span style="background-color: #eeeeee;">この関数は、字句を読み込み、currentTokenにtokenの種類、NUMの場合に tokenValに値を返す。</span></blockquote>
だから、Scheme版は本当に値を返してますが、<a href="http://www.hpcs.cs.tsukuba.ac.jp/~msato/lecture-note/comp-lecture/ex1/getToken.c">オリジナルのCコード</a>は返してませんね。大域変数を書き換えてるわけで、副作用目的の、要するに「手続き」がこのC版<code>getToken</code>の正体です。大体、関数の型が<code>void</code>ですしね。<br />
Scheme版<code>getToken</code>の動作は次のようになります。<br />
<br />
<script src="https://gist.github.com/anonymous/68dd9956d637e288d2cc.js"></script><br />
"12 + 3 - 4"と言う入力を分解して、要素が数値の場合は<code>tokenVal</code>としてその値を返し(数値じゃない場合は0)、それとその<code>tokenVal</code>の「情報」を<code>currentToken</code>として返します。<br />
こういうテストがC言語だとやりづらいトコロです(<code>main</code>関数が無い状態だとどう動作してるんだか分かったモンじゃないし、要コンパイルなのが自明です)。<br />
<br />
<h2 style="text-align: left;">
BNFと構文木</h2>
<br />
<blockquote class="tr_bq">
では、この「式」というプログラミング言語の構文とはどのようなものであろうか。例えば、次のような規則が構文である。<br />
<blockquote>
<table bgcolor="#CCFFCC" border="0" cellpadding="5"><tbody>
<tr><td><pre> 足し算の式 := 式 +の演算子 式
引き算の式 := 式 -の演算子 式
式 := 数字 | 足し算の式 | 引き算の式
</pre>
</td></tr>
</tbody></table>
</blockquote>
<span style="background-color: #eeeeee;">このような記述を、</span><em>BNF (Backus Naur Form または Buckus Normal Form) </em><span style="background-color: #eeeeee;">という。</span><br />
このような構造を反映するデータ構造を作るのが、構文解析である。図に示す。<br />
<blockquote>
<table bgcolor="#CCFFCC" border="0" cellpadding="5"><tbody>
<tr><td><img src="http://www.hpcs.cs.tsukuba.ac.jp/~msato/lecture-note/comp-lecture/fig1/fig1-5.gif" /></td></tr>
</tbody></table>
</blockquote>
<span style="background-color: #eeeeee;">構文解析のデータ構造は、以下のような構造体を作る。これを<a href="http://www.hpcs.cs.tsukuba.ac.jp/~msato/lecture-note/comp-lecture/ex1/exprParser.h">exprParser.h</a>に 定義しておく。</span></blockquote>
<br />
さて、ここでまた悩んだんですよね~。Schemeでも<code>record-type</code>を使って構造体で作るべきか?<br />
大体、構造体絡むと破壊的変更が避けられなくなったりするんですよねぇ。しかも<a href="http://www.hpcs.cs.tsukuba.ac.jp/~msato/lecture-note/comp-lecture/ex1/exprParser.h">Cのコード</a>見ると、何だか循環参照のように見えるし・・・(<a href="https://twitter.com/kotatsu_mi/status/570269881016274944">実は違う</a>そうですが)。<br />
結局、わざわざ構造体で構文木を定義するのは止めました。これはリストを持たない貧弱なCだから必要なんであって、SchemeなんかのLisp系言語では必要ない。要するに直接構文木(らしきもの)をリストを使って直接生成してやれば良い、って事です。<br />
つまり、例えば上のような<br />
<br />
<blockquote class="tr_bq">
[12の数字] [+演算子] [3の数字] [4の数字] [-演算子] [終わり]</blockquote>
<br />
と言うトークン列に対して<br />
<br />
<blockquote class="tr_bq">
'([-演算子] ([+演算子] [12の数字] [3の数字]) [4の数字]) </blockquote>
<blockquote class="tr_bq">
</blockquote>
みたいなリストを生成して返してやれば良い、って事です。しかも、連想リストを生成するようにしてみます。<br />
<br />
<blockquote class="tr_bq">
<span style="background-color: #eeeeee;">この構文木を作るプログラムが、<a href="http://www.hpcs.cs.tsukuba.ac.jp/~msato/lecture-note/comp-lecture/ex1/readExpr.c">readExpr.c</a>である。 このプログラムでは、<a href="http://www.hpcs.cs.tsukuba.ac.jp/~msato/lecture-note/comp-lecture/ex1/exprParser.h">exprParser.h</a>で定義されて いるASTを使って、構文木を作っている。このデータ構造は 式の場合は、演算子とその左辺の式と右辺の式を持つ。数字の場合はこれらを 使わずに値のみを格納する。tokenを読むたびに、データ構造を作っている。</span> </blockquote>
ASTは定義しない事にしましたが、tokenを読むたびにデータ構造を作り出して、構文木のデータ構造を作って返すのはLispではお手の物です。次が等価のScheme版<code>readExpr</code>です。<br />
<br />
<script src="https://gist.github.com/anonymous/ab8e3f246be50d85c0d4.js"></script><br />
<a href="http://www.hpcs.cs.tsukuba.ac.jp/~msato/lecture-note/comp-lecture/ex1/readExpr.c">オリジナルのコード</a>だと、大域変数が<code>getToken</code>呼び出す度に書き換えられてて、その破壊的変更をアテにするプログラミングな為、これを関数プログラミングで再現するにはどうすりゃエエんだ、ってんで結構悩んだんですよねぇ。本当だったらもっと綺麗に書けたんじゃねぇの、って若干心残りがあるんですが、一応オリジナルのロジックを出来るだけ尊重するようにはしてみました。<br />
また、<a href="http://www.hpcs.cs.tsukuba.ac.jp/~msato/lecture-note/comp-lecture/ex1/readExpr.c">オリジナル版</a>だと、破壊的変更前提の無引数の手続きなんですが、Scheme版だと、やっぱり<code>tokenVal</code>と<code>currentToken</code>を受け取るプロシージャにしています。<br />
では、動作を見てみます。<br />
<br />
<script src="https://gist.github.com/anonymous/ca6eb0dcd4d4f15d013a.js"></script><br />
さっきの設計と真逆になってるように見えますが、これで良いのです。そもそも、連想リストだと順序には意味がありません。<br />
基本的な構文構造を表現する連想リスト(構文木リスト = AST)は<br />
<br />
<blockquote class="tr_bq">
'((op . currentToken) (val . tokenVal) (left . 左の枝) (right . 右の枝))</blockquote>
<br />
となっていて、valは数値の時にしか生成されず、また、leftやrightの中も再帰的にASTが収まっていきます。<br />
<br />
<br />
<h2 style="text-align: left;">
解釈実行: インタプリター</h2>
<blockquote class="tr_bq">
この構文木を解釈して実行する、すなわちインタプリターをつくってみること にする。その動作は、<br />
<ol>
<li>式が数字であれば、その数字を返す。</li>
<li>式が演算子を持つ演算式であれば、左辺と右辺を解釈実行した結果を、 演算子の演算を行い、その値を返す。</li>
</ol>
<span style="background-color: #eeeeee;">このプログラムが<a href="http://www.hpcs.cs.tsukuba.ac.jp/~msato/lecture-note/comp-lecture/ex1/evalExpr.c">evalExpr.c</a>である。 <a href="http://www.hpcs.cs.tsukuba.ac.jp/~msato/lecture-note/comp-lecture/ex1/evalExpr.c">evalExpr.c</a>は、構文木ASTを解釈して、解釈する。</span><br />
<ol>
<li>数字のASTつまり、opがNUMであれば、その値を返す。</li>
<li>演算式であれば、左辺を評価した値と右辺を評価した値をopに格納さ れている演算子にしたがって、計算を行う。</li>
</ol>
<span style="background-color: #eeeeee;">これらは再帰的に呼び出しが行われていることに注意しよう。</span></blockquote>
まあ、今までも何度かインタプリタは書いてきましたが、構文木を使って、ってのは初めてですね。<br />
<br />
<script src="https://gist.github.com/anonymous/a25f11ffd3ce3d803623.js"></script><br />
構造はほぼ<a href="http://www.hpcs.cs.tsukuba.ac.jp/~msato/lecture-note/comp-lecture/ex1/evalExpr.c">オリジナルのコード</a>と同じです。特に手を加えてはいません。<br />
しっかし、ひっさしぶりに末尾再帰じゃない再帰コード書いたんで気持ち悪いですね(笑)。<a href="http://www.hpcs.cs.tsukuba.ac.jp/~msato/lecture-note/comp-lecture/ex1/evalExpr.c">オリジナルのC版</a>も、これじゃあ大して効率良く無いんじゃないでしょうか。あー、そうか、コンパイラ書く為の前フリか(笑)。<br />
<code>evalExpr</code>の動作テストは以下の通り。<br />
<br />
<script src="https://gist.github.com/anonymous/9a488042dd9a625b7dd9.js"></script><br />
キチンと計算されてますね。<br />
<br />
<blockquote class="tr_bq">
<span style="background-color: #eeeeee;">mainプログラムでは、関数<a href="http://www.hpcs.cs.tsukuba.ac.jp/~msato/lecture-note/comp-lecture/ex1/readExpr.c">readExpr</a>を呼び、構文木を作り、それを関数 <a href="http://www.hpcs.cs.tsukuba.ac.jp/~msato/lecture-note/comp-lecture/ex1/evalExpr.c">evalExpr</a>で解釈実行して、その結果を出力する。これが、インタプリターであ る。先のプログラムと大きく違うのは、式の意味を表す構文木が内部に生成さ れていることである。この構文木の意味を解釈するのがインタプリターである。 (<a href="http://www.hpcs.cs.tsukuba.ac.jp/~msato/lecture-note/comp-lecture/ex1/readExpr.c">readExpr</a>では1つだけ先読みが必要であるので、<a href="http://www.hpcs.cs.tsukuba.ac.jp/~msato/lecture-note/comp-lecture/ex1/getToken.c">getToken</a>を呼び出している)</span></blockquote>
<br />
うーん、正直、なんで<br />
<blockquote class="tr_bq">
<span style="background-color: #fffccc;"><code>if(currentToken != EOL){</code></span></blockquote>
<pre style="background-color: #fffccc;"> printf("error: EOL expected\n");
exit(1);
}</pre>
<br />
なんてのがあるんだか分からないですね。これねぇ方が動くんだけど・・・。<br />
まあいいや、上記のコード部分を除いてSchemeで書いた<code>main</code>プロシージャが次になります。<br />
<br />
<script src="https://gist.github.com/anonymous/2b7cf256e90170aad170.js"></script><br />
さっき書いたテストまんまそのまんまですね。<br />
では動作確認です。<br />
<br />
<script src="https://gist.github.com/anonymous/bfd2f91e90e4e2979b23.js"></script><br />
完璧ですね。<br />
ではScheme版インタプリタのソースコード全容を。<br />
<br />
<script src="https://gist.github.com/anonymous/744c0131d6ed0f58bd61.js"></script><br />
では、次はいよいよコンパイラ、です。<br />
<br />
<br />
<h2 style="text-align: left;">
コンパイラとは</h2>
<br />
<blockquote class="tr_bq">
次にコンパイラをつくってみる。コンパイラとは、解釈実行する代わりに、実 行すべきコード列に変換するプログラムである。実行すべきコード列は、通常、 アセンブリ言語(機械語)であるが、そのほかのコードでもよい。中間コード として、スタックマシンのコードを仮定することにする。スタックマシンは以 下のコードを持つことにする。<br />
<ul>
<li>PUSH n : 数字nをスタックにpushする。</li>
<li>ADD : スタックの上2つの値をpopし、それらを加算した結果をpushする。</li>
<li>SUB : スタックの上2つの値をpopし、減算を行い、pushする。</li>
<li>PRINT: スタックの値をpopし、出力する。</li>
</ul>
<br />
コンパイラは、このスタックマシンのコードを使って、式を実行するコード列 を作る。例えば、図で示した例の式12+3-4は下のようなコードになる。<br />
<blockquote>
<table bgcolor="#FFFCCC" border="0" cellpadding="5"><tbody>
<tr><td><pre> PUSH 12
PUSH 3
ADD
PUSH 4
SUB
PRINT
</pre>
</td></tr>
</tbody></table>
</blockquote>
<span style="background-color: #eeeeee;">スタックマシンでの実行は以下のように行われる。</span><br />
<blockquote>
<table bgcolor="#CCFFCC" border="0" cellpadding="5"><tbody>
<tr><td><img src="http://www.hpcs.cs.tsukuba.ac.jp/~msato/lecture-note/comp-lecture/fig1/fig1-6.gif" /></td></tr>
</tbody></table>
</blockquote>
<br />
stackCode.hには、コードとその列を格納する領域を定義してある。</blockquote>
<script src="https://gist.github.com/anonymous/f134cdd36f4fef0ac65b.js"></script><br />
この辺の定義もSchemeには要らないですね。シンボルとリストで凌ぎましょう。<br />
<blockquote class="tr_bq">
コンパイルの手順は、以下のようになる。<br />
<ol>
<li>式が数字であれば、その数字をpushするコードを出す。</li>
<li>式が演算であれば、左辺と右辺をコンパイルし、それぞれの結果をスタッ クにつむコードを出す。その後、演算子に対応したスタックマシンのコードを 出す。</li>
<li>式のコンパイルしたら、PRINTのコードを出しておく。</li>
</ol>
<span style="background-color: #eeeeee;">この中間コードを生成するのが、<a href="http://www.hpcs.cs.tsukuba.ac.jp/~msato/lecture-note/comp-lecture/ex1/compileExpr.c">compileExpr.c</a>である。構文木を入力して、 再帰的に上のアルゴリズムを実行する。コードはCodesという配列に格納して おく。</span></blockquote>
さて、<code>compileExpr</code>ですが、Scheme版では2つに分けました。<br />
<br />
<script src="https://gist.github.com/anonymous/44883f76309f25567ca6.js"></script><br />
一つは構文木を受け取って、上のロジックに従って命令のリストを生成する<code>Codes</code>、もう一つは構文木を受け取って<code>Codes</code>を呼び出し、結果のリストを反転させた後、<code>vector</code>(Cで言う配列にあたる)に変換する<code>compileExpr</code>です。<br />
ちなみに、<code>Codes</code>が全体的に生成するのは、見た目は連想リストですが、連想リストではありません。と言うのも、今回生成するのはアセンブリ的なリストなんで「順序が重要」だからです。連想リストはハッシュ的に順序は重要じゃないんで、リストとして順序を保持したままベクタへ変換する必要性があるから、です(ただし、要素は連想リストです)。<br />
では動作テストです。<br />
<br />
<script src="https://gist.github.com/anonymous/e702772595bb749953af.js"></script><br />
<br />
見て分かる通り、"12 + 3 - 4"と言う入力が、opcodeとoperandと言う2つのキーを持つ、連想リストを5要素としたベクタ(配列)に変換されています。ベクタの番号0~4は結果、実行順序を表してる事になりますね。<br />
<br />
<blockquote class="tr_bq">
<span style="background-color: #eeeeee;">コード生成では、ここではスタックマシンのコードをCに直して出力すること にしよう。Cで実行させるために、mainにいれておくことにする。このプログ ラムが、<a href="http://www.hpcs.cs.tsukuba.ac.jp/~msato/lecture-note/comp-lecture/ex1/codeGen.c">codeGen.c</a>である。</span></blockquote>
<br />
Cで書いてきたのにCで出力する、なんつーのはバカっぽいな、とか思ったんですが(笑)、郷に入りては郷に従え、でSchemeで書いてきたのにSchemeで出力します(爆)。<br />
ちなみに、最初は、<a href="http://www.hpcs.cs.tsukuba.ac.jp/~msato/lecture-note/comp-lecture/ex1/codeGen.c">オリジナルのコード</a>に従って書いてたんですが、上手く動いたのをきっかけにしてもうちょっと欲が出てきたんですね。<br />
<a href="http://www.hpcs.cs.tsukuba.ac.jp/~msato/lecture-note/comp-lecture/ex1/codeGen.c">元のコード</a>にはいくつかちょっと特徴があります。<br />
<br />
<ol style="text-align: left;">
<li>スタックマシンが別にあるわけじゃなくって、スタックマシン自体も毎回コンパイルで生成してる。</li>
<li>printfが多用されているが、結果的にCコードである「文字列」を生成してるだけである。従って本来なら、Cコードを生成する文字列をでっち上げるのが実は大半の本質的作業である。</li>
</ol>
<div>
つまり、SchemeでSchemeコードを生成するにせよ</div>
<div>
<ol style="text-align: left;">
<li>スタックマシン自体を毎回生成してるようなコードを吐いて構わない。</li>
<li>結局文字列を生成、加工するだけで良い</li>
</ol>
<div>
と言う2点が極めて重要なんです。それでスタックマシン用のコードをSchemeのコードとして変換出来るわけです。</div>
</div>
<div>
それで、もう一つあって、最初はSchemeコードを生成する際、これは大方、破壊的変更を多用した「いわゆる」スタックマシンをSchemeで書いてるように生成すれば良いのかな、って思ってたんですが、生成されるコードも関数プログラミング様式で生成出来ないか、って思い直したわけです。</div>
<div>
初め、それは大変なんじゃないか、って思ってたんですが、出力を噛まさずに文字列だけで操作するなら、むしろ再帰と相性が良い、ってのが分かったんですね。</div>
<div>
っつーか、Lispなんかの関数プログラミングだと、プログラミングやってる側だと書くのも読むのも大変になるネストの深さになるわけですが、そこはコンパイラ、全く人間の「可読性」関係無く文字列操れるじゃん、ってぇんで書いてみたのが次のコードになります。</div>
<div>
<br /></div>
<div>
<script src="https://gist.github.com/anonymous/978fc8e7ad3a871a4bac.js"></script><br /></div>
<div>
<br /></div>
<div>
ちょっとどういうSchemeコードを生成するのか、見てみましょうか。<br />
<br />
<script src="https://gist.github.com/anonymous/f80117966efdf64a3960.js"></script><br />
最後の<code>(codeGen code)</code>ってのでSchemeコードを生成するわけですね。<br />
前半の部分は言わばテンプレで、毎回<code>codeGen</code>が呼び出される度に「スタックマシン」を生成します。<br />
実際に<code>codeGen</code>が毎回生成してるのが<code>main</code>プロシージャの部分で、結果、<br />
<br />
<blockquote class="tr_bq">
"12 + 3 - 4"</blockquote>
<br />
と言う入力が、<br />
<br />
<blockquote class="tr_bq">
<code></code><br />
<pre><code>(define (main)
(sub
(push 4
(add
(push 3
(push 12
'()))))))</code></pre>
</blockquote>
に書き換えられてて、これはホント、そのまま関数プログラミングでのスタイルです。空リストをスタックに見立てて、内側のプロシージャは外側へと結果を返して、外側のプロシージャはそれを引数として受け取って・・・って連鎖してるわけですね。<br />
ぶっちゃけ、人としてはあまり書きたくないスタイルですが(笑)、コンパイラなら別にへっちゃらで言われた通りに変換していってくれて、実はコード生成は、再帰を使う限り、こっちの方が(この例だと)簡単なんじゃないか、ってカンジです。<br />
<br />
<blockquote class="tr_bq">
<span style="background-color: #eeeeee;">コンパイラの<a href="http://www.hpcs.cs.tsukuba.ac.jp/~msato/lecture-note/comp-lecture/ex1/compiler.c">main</a>プログラムであるが、<a href="http://www.hpcs.cs.tsukuba.ac.jp/~msato/lecture-note/comp-lecture/ex1/readExpr.c">readExpr</a>まではインタープリタと同じ である。標準出力に出力されるプログラムに適当に名前をつけ(たとえば、 output.c)これをCコンパイラでコンパイルして実行すればよい。(assembler のファイルの場合はasコマンドでコンパイルする。)</span></blockquote>
<br />
そう、<code>display</code>せずにファイルに書き出せば、要するにコンパイラとして機能する、って事です。これは面白い。<br />
<br />
<script src="https://gist.github.com/anonymous/b5aed02b32d16112e4cf.js"></script><br />
<br />
例えば、実験として、次のような式を入力してもガンガン「関数型」のコードに変換してくれます。<br />
<br />
<script src="https://gist.github.com/anonymous/146a025205abeca34923.js"></script><br /></div>
すげぇバカバカしいんですが、面白いです(笑)。なかなか構文解析とかコンパイラ書くのって面白いな、って感動しました。<br />
<br />
コンパイラの全ソースコードは次のようなものです。<br />
<br />
<script src="https://gist.github.com/anonymous/ce5946cf26a94671325b.js"></script><br />
<br />
ちょっと暫くこの辺のネタで遊んでみますかね。</div>
cametan_001http://www.blogger.com/profile/04882440058326617382noreply@blogger.com0tag:blogger.com,1999:blog-4018043311243103708.post-33341199761135813122015-02-17T02:16:00.000+09:002015-02-17T02:16:15.510+09:00Schemeでスタックマシンの基礎<div dir="ltr" style="text-align: left;" trbidi="on">
<a href="http://ja.wikipedia.org/wiki/%E3%82%A6%E3%82%A3%E3%82%B6%E3%83%BC%E3%83%89%E3%83%AA%E3%82%A3">Wizardry</a>ってゲームが好きです。<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="http://wizardry.acquire.co.jp/thanks/images/game.gif" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="http://wizardry.acquire.co.jp/thanks/images/game.gif" height="255" width="320" /></a></div>
<br />
まあ、あまりにも有名なゲームなんで知らない人は多分いないと思うんですが、一応ちょっと解説してみますか。<br />
世界で初めて、商用として成功したRPG(ロールプレイングゲーム)ですね。<br />
<br />
<blockquote class="tr_bq">
注: 良く「世界初のRPG」って記述が成されますが、これは嘘です。実は世界初のRPGは<a href="http://ja.wikipedia.org/wiki/PLATO">PLATO</a>と言うメインフレームのプラットフォームで作られていて、それは1974年頃の事でした。WizardryはそのPLATOで1977年頃に作られたMMORPG(ビックリするかもしれないけど、ネット時代より遥か以前にMMORPGは既に存在していた!)である<a href="http://crpgaddict.blogspot.jp/2013/10/game-12-oubliette-1977.html">Oubliette</a>と言うゲームをApple II上で1人でプレイできるように「改良した」もの、って言って良いです。</blockquote>
さて、このWizardryってのは色々なマシンに移植されてるわけですが。ファミコンみたいなゲーム専用機以外でもザーッと挙げてみると、<br />
<br />
<br />
<ul style="text-align: left;">
<li>Apple II</li>
<li>Apple Macintosh</li>
<li>SHARP MZ-2500</li>
<li>SHARP X1/Turbo</li>
<li>富士通 FM-7</li>
<li>富士通 FM-77</li>
<li>NEC PC-8801</li>
<li>NEC PC-9801</li>
<li>MSX-2</li>
<li>Commodore 64</li>
<li>Commodore 128</li>
<li>IBM-PC</li>
</ul>
<div>
と物凄い数の移植なんですね。</div>
<div>
これだと物凄い数のプログラマが物凄く苦労して移植したんじゃないか、って思うでしょう。ところが、メインプログラマの<a href="http://ja.wikipedia.org/wiki/%E3%83%AD%E3%83%90%E3%83%BC%E3%83%88%E3%83%BB%E3%82%A6%E3%83%83%E3%83%89%E3%83%98%E3%83%83%E3%83%89">ロバート・ウッドヘッド</a>氏の話によると、</div>
<div>
<br /></div>
<div>
「殆ど(80%くらい?)ソースコードは同じなんだ。違うのは画像出力とか、機械によって差があるところだけだね。」</div>
<div>
<br /></div>
<div>
との事です。</div>
<div>
当時のゲームプログラムだとBASIC、あるいはスピードが欲しい場合は直接アセンブリでコーディングするのが普通だったらしいんですが、Wizardryの設計の優れたところ、と言うのは、<a href="http://ja.wikipedia.org/wiki/Pascal">Pascal</a>を使った構造化プログラミングで作ったほぼ最初の商用製品だ、と言うトコロでしょう。完全にシナリオデータとゲーム本体のプログラムを分けているらしく、外国語へのポーティングも必要最小限の労力で行えるように設計されているらしく、恐らく凄く綺麗な「教科書的な」プログラミングの塊なんじゃないでしょうか。</div>
<div>
ここで使われたPascal処理系を<a href="http://ja.wikipedia.org/wiki/UCSD_Pascal">UCSD Pascal</a>と言います。これは仮想マシン上で動くように設計されたPascal処理系の代表格で、まさに当時の"Write Once, Run Anywhere"を実現してたようですね・・・そう、当時はJava的な存在だったわけです。</div>
<div>
<br /></div>
<blockquote class="tr_bq">
注: C言語も「マシン間の差異があってもソースの移植性を極力大事にする」意図で設計されていますが、随分と違うアプローチで、元々Pascalは「仮想マシンを前提として動かす」と言うアプローチになってました。しかも、C言語は原則的に16bit機以上が対象で、黎明期の8bitのパーソナルコンピュータ上だと「動かしづらい」プログラミング言語だった模様です。その辺、当時の環境だとPascalの方がメリットが大きかったのでしょう。</blockquote>
<br />
さて、そのUCSD Pascal。Wikipediaではこんな記述が成されていますね。<br />
<br />
<blockquote class="tr_bq">
<a href="http://ja.wikipedia.org/wiki/CPU" style="background: none rgb(255, 255, 255); color: #0b0080; font-family: sans-serif; font-size: 15.1040010452271px; line-height: 19.3331203460693px; text-decoration: none;" title="CPU">CPU</a><span style="background-color: white; color: #252525; font-family: sans-serif; font-size: 15.1040010452271px; line-height: 19.3331203460693px;">の異なる</span><a href="http://ja.wikipedia.org/wiki/%E3%83%91%E3%83%BC%E3%82%BD%E3%83%8A%E3%83%AB%E3%82%B3%E3%83%B3%E3%83%94%E3%83%A5%E3%83%BC%E3%82%BF" style="background: none rgb(255, 255, 255); color: #0b0080; font-family: sans-serif; font-size: 15.1040010452271px; line-height: 19.3331203460693px; text-decoration: none;" title="パーソナルコンピュータ">パーソナルコンピュータ</a><span style="background-color: white; color: #252525; font-family: sans-serif; font-size: 15.1040010452271px; line-height: 19.3331203460693px;">上で動作するために、</span><a class="mw-redirect" href="http://ja.wikipedia.org/wiki/P-Machine" style="background: none rgb(255, 255, 255); color: #0b0080; font-family: sans-serif; font-size: 15.1040010452271px; line-height: 19.3331203460693px; text-decoration: none;" title="P-Machine">P-Machine</a><span style="background-color: white; color: #252525; font-family: sans-serif; font-size: 15.1040010452271px; line-height: 19.3331203460693px;">と呼ばれる</span><a class="mw-redirect" href="http://ja.wikipedia.org/wiki/%E3%83%90%E3%83%BC%E3%83%81%E3%83%A3%E3%83%AB%E3%83%9E%E3%82%B7%E3%83%B3" style="background: none rgb(255, 255, 255); color: #0b0080; font-family: sans-serif; font-size: 15.1040010452271px; line-height: 19.3331203460693px; text-decoration: none;" title="バーチャルマシン">仮想マシン</a><span style="background-color: white; color: #252525; font-family: sans-serif; font-size: 15.1040010452271px; line-height: 19.3331203460693px;">を使用する。コンパイラは</span><a href="http://ja.wikipedia.org/wiki/%E3%83%97%E3%83%AD%E3%82%B0%E3%83%A9%E3%83%A0_(%E3%82%B3%E3%83%B3%E3%83%94%E3%83%A5%E3%83%BC%E3%82%BF)" style="background: none rgb(255, 255, 255); color: #0b0080; font-family: sans-serif; font-size: 15.1040010452271px; line-height: 19.3331203460693px; text-decoration: none;" title="プログラム (コンピュータ)">プログラム</a><span style="background-color: white; color: #252525; font-family: sans-serif; font-size: 15.1040010452271px; line-height: 19.3331203460693px;">をそれぞれのCPU用の</span><a href="http://ja.wikipedia.org/wiki/%E6%A9%9F%E6%A2%B0%E8%AA%9E" style="background: none rgb(255, 255, 255); color: #0b0080; font-family: sans-serif; font-size: 15.1040010452271px; line-height: 19.3331203460693px; text-decoration: none;" title="機械語">機械語</a><span style="background-color: white; color: #252525; font-family: sans-serif; font-size: 15.1040010452271px; line-height: 19.3331203460693px;">に翻訳するのではなく、P-Machineの機械語であるP-Codeに翻訳する。そのため、P-Codeの仮想マシンを実装すればどのようなパーソナルコンピュータ上でも実行可能であった。</span></blockquote>
<br />
かっちょいい(笑)!仮想マシンですよ、仮想マシン(笑)。<br />
問題は仮想マシンってのは実装が簡単なんですかねぇ。次のような記述が続きます。<br />
<br />
<blockquote class="tr_bq">
<span style="background-color: white; color: #252525; font-family: sans-serif; font-size: 15.1040010452271px; line-height: 19.3331203460693px;">P-Machineは典型的な</span><a href="http://ja.wikipedia.org/wiki/%E3%82%B9%E3%82%BF%E3%83%83%E3%82%AF%E3%83%9E%E3%82%B7%E3%83%B3" style="background: none rgb(255, 255, 255); color: #0b0080; font-family: sans-serif; font-size: 15.1040010452271px; line-height: 19.3331203460693px; text-decoration: none;" title="スタックマシン">スタックマシン</a><span style="background-color: white; color: #252525; font-family: sans-serif; font-size: 15.1040010452271px; line-height: 19.3331203460693px;">で、様々な処理を主に</span><a href="http://ja.wikipedia.org/wiki/%E3%82%B9%E3%82%BF%E3%83%83%E3%82%AF" style="background: none rgb(255, 255, 255); color: #0b0080; font-family: sans-serif; font-size: 15.1040010452271px; line-height: 19.3331203460693px; text-decoration: none;" title="スタック">スタック</a><span style="background-color: white; color: #252525; font-family: sans-serif; font-size: 15.1040010452271px; line-height: 19.3331203460693px;">上で行う</span><a href="http://ja.wikipedia.org/wiki/%E3%82%A2%E3%83%BC%E3%82%AD%E3%83%86%E3%82%AF%E3%83%81%E3%83%A3" style="background: none rgb(255, 255, 255); color: #0b0080; font-family: sans-serif; font-size: 15.1040010452271px; line-height: 19.3331203460693px; text-decoration: none;" title="アーキテクチャ">アーキテクチャ</a><span style="background-color: white; color: #252525; font-family: sans-serif; font-size: 15.1040010452271px; line-height: 19.3331203460693px;">を持っていた。</span></blockquote>
スタックマシン・・・何じゃそれ、なんですが(笑)。<br />
<a href="http://ja.wikipedia.org/wiki/%E3%82%B9%E3%82%BF%E3%83%83%E3%82%AF%E3%83%9E%E3%82%B7%E3%83%B3">Wikipediaのスタックマシン</a>の項目読んでてもイマイチピンと来ない。 やっぱ一回実装してみるに限りますね。<br />
元々、CPUの動作とかは全く知らない門外漢なんですが、そろそろその辺勉強しても良い頃かもしれません。<br />
ってなわけで、ネットで検索するとJavaで書かれた「<a href="http://www.atmarkit.co.jp/ait/articles/0702/23/news126.html">仮想計算機を作ろう</a>」と言うページを見つけたんで、Java書けないんですが(笑)、何とか読み下して、Schemeで簡単なスタックマシンを実装してみたいと思います。<br />
<br />
<h2 style="text-align: left;">
スタックとは何か?</h2>
<div>
知るか(笑)。</div>
<div>
いや、昔勉強した事あった、って言えばあったんですが、「一体何の為にこれやってんのか」ってのがサッパリ分からないんで、すっかり忘れてます(笑)。Lisp系だと何でもリストを使えば済む、ってんであんまマジメに考えてなかったんですよねぇ。改めて勉強です。</div>
<div>
<br /></div>
<blockquote class="tr_bq">
<span style="background-color: white; font-family: Meiryo, メイリオ, ArialMT, 'Hiragino Kaku Gothic Pro', 'ヒラギノ角ゴ Pro W3', Osaka, Verdana, 'MS Pゴシック'; font-size: 16px; line-height: 26px;">仮想スタックマシンを実装するに当たって、スタックというデータ構造について理解している必要がありますから、まずはこれについて、簡単なプログラムを交えながら解説することにします。</span></blockquote>
<br />
はいはい。<br />
<blockquote class="tr_bq">
<span style="background-color: white; font-family: Meiryo, メイリオ, ArialMT, 'Hiragino Kaku Gothic Pro', 'ヒラギノ角ゴ Pro W3', Osaka, Verdana, 'MS Pゴシック'; font-size: 16px; line-height: 26px;">スタックは</span><strong style="background-color: white; font-family: Meiryo, メイリオ, ArialMT, 'Hiragino Kaku Gothic Pro', 'ヒラギノ角ゴ Pro W3', Osaka, Verdana, 'MS Pゴシック'; font-size: 16px; line-height: 26px;">データ構造</strong><span style="background-color: white; font-family: Meiryo, メイリオ, ArialMT, 'Hiragino Kaku Gothic Pro', 'ヒラギノ角ゴ Pro W3', Osaka, Verdana, 'MS Pゴシック'; font-size: 16px; line-height: 26px;">の1つで、</span><strong style="background-color: white; font-family: Meiryo, メイリオ, ArialMT, 'Hiragino Kaku Gothic Pro', 'ヒラギノ角ゴ Pro W3', Osaka, Verdana, 'MS Pゴシック'; font-size: 16px; line-height: 26px;">LIFO</strong><span style="background-color: white; font-family: Meiryo, メイリオ, ArialMT, 'Hiragino Kaku Gothic Pro', 'ヒラギノ角ゴ Pro W3', Osaka, Verdana, 'MS Pゴシック'; font-size: 16px; line-height: 26px;">(Last In First Out)という性質を持っています。これは、最後に入れたデータを最初に取り出すことができるという意味になります。</span></blockquote>
何か、んな事言ってたなぁ。<br />
<blockquote class="tr_bq">
<span style="background-color: white; font-family: Meiryo, メイリオ, ArialMT, 'Hiragino Kaku Gothic Pro', 'ヒラギノ角ゴ Pro W3', Osaka, Verdana, 'MS Pゴシック'; font-size: 16px; line-height: 26px;">ボール(値)を上からしか出し入れできない、不透明な長い箱をイメージするとよいでしょう。このような箱からボールを取り出すには、最後に入れたボール(値)からしか取り出せないということになります。</span></blockquote>
<div class="separator" style="clear: both; text-align: center;">
<a href="http://image.itmedia.co.jp/ait/articles/0702/23/r5compiler03_01.gif" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="http://image.itmedia.co.jp/ait/articles/0702/23/r5compiler03_01.gif" height="117" width="320" /></a></div>
<br />
<blockquote class="tr_bq">
<span style="background-color: white; font-family: Meiryo, メイリオ, ArialMT, 'Hiragino Kaku Gothic Pro', 'ヒラギノ角ゴ Pro W3', Osaka, Verdana, 'MS Pゴシック'; font-size: 16px; line-height: 26px;">値をスタックへ格納する操作は</span><strong style="background-color: white; font-family: Meiryo, メイリオ, ArialMT, 'Hiragino Kaku Gothic Pro', 'ヒラギノ角ゴ Pro W3', Osaka, Verdana, 'MS Pゴシック'; font-size: 16px; line-height: 26px;">push</strong><span style="background-color: white; font-family: Meiryo, メイリオ, ArialMT, 'Hiragino Kaku Gothic Pro', 'ヒラギノ角ゴ Pro W3', Osaka, Verdana, 'MS Pゴシック'; font-size: 16px; line-height: 26px;">と呼ばれ、値をスタックから取り出す操作は</span><strong style="background-color: white; font-family: Meiryo, メイリオ, ArialMT, 'Hiragino Kaku Gothic Pro', 'ヒラギノ角ゴ Pro W3', Osaka, Verdana, 'MS Pゴシック'; font-size: 16px; line-height: 26px;">pop</strong><span style="background-color: white; font-family: Meiryo, メイリオ, ArialMT, 'Hiragino Kaku Gothic Pro', 'ヒラギノ角ゴ Pro W3', Osaka, Verdana, 'MS Pゴシック'; font-size: 16px; line-height: 26px;">と呼ばれます。この2つの基本操作でスタックへ格納するデータをコントロールできます。</span><strong style="background-color: white; font-family: Meiryo, メイリオ, ArialMT, 'Hiragino Kaku Gothic Pro', 'ヒラギノ角ゴ Pro W3', Osaka, Verdana, 'MS Pゴシック'; font-size: 16px; line-height: 26px;">オレンジ矢印</strong><span style="background-color: white; font-family: Meiryo, メイリオ, ArialMT, 'Hiragino Kaku Gothic Pro', 'ヒラギノ角ゴ Pro W3', Osaka, Verdana, 'MS Pゴシック'; font-size: 16px; line-height: 26px;">が値を積むpushの操作を表し、</span><strong style="background-color: white; font-family: Meiryo, メイリオ, ArialMT, 'Hiragino Kaku Gothic Pro', 'ヒラギノ角ゴ Pro W3', Osaka, Verdana, 'MS Pゴシック'; font-size: 16px; line-height: 26px;">グレイ矢印</strong><span style="background-color: white; font-family: Meiryo, メイリオ, ArialMT, 'Hiragino Kaku Gothic Pro', 'ヒラギノ角ゴ Pro W3', Osaka, Verdana, 'MS Pゴシック'; font-size: 16px; line-height: 26px;">が値を取り出すpopの操作を表しています。次に取り出せるデータを参照するための</span><strong style="background-color: white; font-family: Meiryo, メイリオ, ArialMT, 'Hiragino Kaku Gothic Pro', 'ヒラギノ角ゴ Pro W3', Osaka, Verdana, 'MS Pゴシック'; font-size: 16px; line-height: 26px;">peek</strong><span style="background-color: white; font-family: Meiryo, メイリオ, ArialMT, 'Hiragino Kaku Gothic Pro', 'ヒラギノ角ゴ Pro W3', Osaka, Verdana, 'MS Pゴシック'; font-size: 16px; line-height: 26px;">という操作が提供されることもあります。</span></blockquote>
<blockquote class="tr_bq">
<span style="background-color: white; font-family: Meiryo, メイリオ, ArialMT, 'Hiragino Kaku Gothic Pro', 'ヒラギノ角ゴ Pro W3', Osaka, Verdana, 'MS Pゴシック'; font-size: 16px; line-height: 26px;">スタックにはどのような値がどういう順番で格納されているのかについては隠ぺいされていて、</span><strong style="background-color: white; font-family: Meiryo, メイリオ, ArialMT, 'Hiragino Kaku Gothic Pro', 'ヒラギノ角ゴ Pro W3', Osaka, Verdana, 'MS Pゴシック'; font-size: 16px; line-height: 26px;">スタックを使用するプログラムからは見えない</strong><span style="background-color: white; font-family: Meiryo, メイリオ, ArialMT, 'Hiragino Kaku Gothic Pro', 'ヒラギノ角ゴ Pro W3', Osaka, Verdana, 'MS Pゴシック'; font-size: 16px; line-height: 26px;">という点も重要です。</span></blockquote>
<blockquote class="tr_bq">
<span style="background-color: white; font-family: Meiryo, メイリオ, ArialMT, 'Hiragino Kaku Gothic Pro', 'ヒラギノ角ゴ Pro W3', Osaka, Verdana, 'MS Pゴシック'; font-size: 16px; line-height: 26px;">このように、スタックでは単純な操作しかできないのですが、後置記法と組み合わせることにより結構複雑な計算を実現できます。基本動作を理解するために、スタックの使い方と簡単な実装方法について見てみましょう。</span></blockquote>
なるほど、です。<br />
次へ進んでみましょうか。<br />
<br />
<h2 style="text-align: left;">
スタックを実装してみる</h2>
<div style="text-align: left;">
まずは、その<code>push</code>と<code>pop</code>を実装してみます。 Schemeだと次のように実装するのが綺麗なんじゃないでしょうか。<br />
<br />
<script src="https://gist.github.com/anonymous/64f705590a3a84209ff0.js"></script><br />
<code>push</code>は事実上<code><a href="http://docs.racket-lang.org/reference/pairs.html#%28def._%28%28quote._~23~25kernel%29._cons%29%29">cons</a></code>ですね。ここは良し、とします。<br />
問題は<code>pop</code>ですか。通常、恐らく大域変数<code>stack</code>をリストで作っておいて、破壊的変更を行う、ってのがこのテのコーディングの前提になるんでしょうが、それは避けます。あくまで関数型プログラミングの範疇で、破壊的変更しない前提で行きます。<br />
が、そうすると困ったことになるんですね。原則、<code>push</code>では<code>stack</code>は仮引数として与えて<code>stack</code>に何らかの操作をした結果を返せばいいわけですが、<code>pop</code>の場合、「取り出した値を返す」のが大事なのか「操作した後の<code>stack</code>を返す」のが大事なのか分かりません。ってかどっちも大事なんですよね~。<br />
こう言う場合多値を用いて両者とも返しちゃう、ってのが一番Schemeらしいでしょう。そう言う実装方針で上のコードは掻きました。<br />
<blockquote class="tr_bq">
<span style="background-color: white; font-family: Meiryo, メイリオ, ArialMT, 'Hiragino Kaku Gothic Pro', 'ヒラギノ角ゴ Pro W3', Osaka, Verdana, 'MS Pゴシック'; font-size: 16px; line-height: 26px;">次に示すような順番でスタックへ値を積んだり、スタックから値を取り出したりしてみます。</span></blockquote>
<div class="separator" style="clear: both; text-align: center;">
<a href="http://image.itmedia.co.jp/ait/articles/0702/23/r5compiler03_02.gif" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="http://image.itmedia.co.jp/ait/articles/0702/23/r5compiler03_02.gif" height="230" width="320" /></a></div>
<blockquote class="tr_bq">
<span style="background-color: white; font-family: Meiryo, メイリオ, ArialMT, 'Hiragino Kaku Gothic Pro', 'ヒラギノ角ゴ Pro W3', Osaka, Verdana, 'MS Pゴシック'; font-size: 16px; line-height: 26px;">ここでは、</span><strong style="background-color: white; font-family: Meiryo, メイリオ, ArialMT, 'Hiragino Kaku Gothic Pro', 'ヒラギノ角ゴ Pro W3', Osaka, Verdana, 'MS Pゴシック'; font-size: 16px; line-height: 26px;">図2</strong><span style="background-color: white; font-family: Meiryo, メイリオ, ArialMT, 'Hiragino Kaku Gothic Pro', 'ヒラギノ角ゴ Pro W3', Osaka, Verdana, 'MS Pゴシック'; font-size: 16px; line-height: 26px;">のボール1をItem1、ボール2をItem2のように表すことにします。</span><strong style="background-color: white; font-family: Meiryo, メイリオ, ArialMT, 'Hiragino Kaku Gothic Pro', 'ヒラギノ角ゴ Pro W3', Osaka, Verdana, 'MS Pゴシック'; font-size: 16px; line-height: 26px;">図2</strong><span style="background-color: white; font-family: Meiryo, メイリオ, ArialMT, 'Hiragino Kaku Gothic Pro', 'ヒラギノ角ゴ Pro W3', Osaka, Verdana, 'MS Pゴシック'; font-size: 16px; line-height: 26px;">の動作例に従い、Item1、Item2、Item3の値を順にスタックへ積んだ(push)後、スタックから値を取り出します(pop)。このとき、一番上にある値はItem3なので、これが取り出されます。</span></blockquote>
<blockquote class="tr_bq">
<span style="background-color: white; font-family: Meiryo, メイリオ, ArialMT, 'Hiragino Kaku Gothic Pro', 'ヒラギノ角ゴ Pro W3', Osaka, Verdana, 'MS Pゴシック'; font-size: 16px; line-height: 26px;">次に、Item4、Item5の値を順にスタックへ積んでから、3回連続でスタックから値を取り出します。このとき、スタックの上から順に値が取り出されるので、Item5、Item4、Item2の順に出てきます。このタイミングでは、スタックにはItem1しか残っていないということになります。</span></blockquote>
<blockquote class="tr_bq">
<span style="background-color: white; font-family: Meiryo, メイリオ, ArialMT, 'Hiragino Kaku Gothic Pro', 'ヒラギノ角ゴ Pro W3', Osaka, Verdana, 'MS Pゴシック'; font-size: 16px; line-height: 26px;">最後に、ここへItem6を積んで、2回連続でスタックから値を取り出します。すると、Item6、Item1の順で値が取り出されます。</span></blockquote>
では、ここで書いてあるような動作をテストするプロシージャ、<code>stacktest</code>を実装してみます。こう言う場合、Schemeだとちょっと汚くなる、っつーかSchemeらしい書き方が難しいんですよね。何せ逐次実行が前提の言語じゃないんで、フラットに書くのが難しい。<br />
一方、所詮動作確認なんで、あんま面倒臭い事考えたくないんで、適当にベタ書きしてみます。<br />
<br />
<script src="https://gist.github.com/anonymous/e12f7b7882ad9f789b24.js"></script><br />
オリジナルのJavaコードに対応させようとすると、大体こんなカンジですかね。Javaなんかは由緒正しい「逐次処理言語」なんで、コードがフラットなんですが、Schemeの場合、特に今回は<code>pop</code>が多値を返す為にこう言うヘンなカンジにならざるを得なかったです。<br />
ポイントは<code><a href="http://www.chino-js.com/ja/tech/srfi/srfi-11/srfi-11.html">let-values</a></code>で、<code>pop</code>が多値を返し、最初の値(つまり取り出した値)を表示用のプロシージャ、<code><a href="https://www.google.co.jp/url?sa=t&rct=j&q=&esrc=s&source=web&cd=1&cad=rja&uact=8&ved=0CCQQygQwAA&url=http%3A%2F%2Fja.wikibooks.org%2Fwiki%2FScheme%23display&ei=PSfhVMq1CcKE8gWV_4Iw&usg=AFQjCNGKSS0bFHlX_J7z6Csi4vKQD5gzBQ&sig2=LQGm-Pqwo01GSNmQxjx_7g&bvm=bv.85970519,d.dGc">display</a></code>に渡して表示させて、残りを<code>stack</code>として次のプロシージャに渡すようになっています。<br />
<br />
<script src="https://gist.github.com/anonymous/066e548c8a98606c02fb.js"></script><br />
<br />
では動作確認してみましょうか。<br />
<br />
<script src="https://gist.github.com/anonymous/d39ad49007541be5a41d.js"></script><br />
<br />
元ページのJavaコードの実行結果と同じになっていますね。確かにLIFOになっています。<br />
<br />
<h2 style="text-align: left;">
仮想スタックマシンを実装する</h2>
<blockquote class="tr_bq">
<span style="background-color: white; font-family: Meiryo, メイリオ, ArialMT, 'Hiragino Kaku Gothic Pro', 'ヒラギノ角ゴ Pro W3', Osaka, Verdana, 'MS Pゴシック'; font-size: 16px; line-height: 26px;">すでに何度か説明しましたが、後置記法で表現された式というのは、スタックを使うと、非常に簡単に計算ができてしまいます。そして、このスタックというデータ構造を実現することは、これまでの説明からも分かるように、それほど難しくはありません。</span></blockquote>
<div class="separator" style="clear: both; text-align: center;">
<a href="http://image.itmedia.co.jp/ait/articles/0702/23/r5compiler02_07.gif" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="http://image.itmedia.co.jp/ait/articles/0702/23/r5compiler02_07.gif" height="259" width="320" /></a></div>
<br />
さいでっか。じゃあ、次行きましょうか。<br />
<br />
<h2 style="text-align: left;">
設計</h2>
<div>
<a href="http://www.atmarkit.co.jp/ait/articles/0702/23/news126.html">オリジナル</a>だとクラスだフィールドだメソッドだ、って書いてるんですが、Schemeにはんなモン無いんで無視します(笑)。そもそも大域変数を破壊的変更する設計にしないんで、フィールドとか要らんでしょ。</div>
<div>
ただ、簡単化の為に次のヤツだけは受けます。</div>
<div>
<br /></div>
<blockquote class="tr_bq">
<span style="background-color: white; font-family: Meiryo, メイリオ, ArialMT, 'Hiragino Kaku Gothic Pro', 'ヒラギノ角ゴ Pro W3', Osaka, Verdana, 'MS Pゴシック'; font-size: 16px; line-height: 26px;">また、</span><strong style="background-color: white; font-family: Meiryo, メイリオ, ArialMT, 'Hiragino Kaku Gothic Pro', 'ヒラギノ角ゴ Pro W3', Osaka, Verdana, 'MS Pゴシック'; font-size: 16px; line-height: 26px;">演算装置</strong><span style="background-color: white; font-family: Meiryo, メイリオ, ArialMT, 'Hiragino Kaku Gothic Pro', 'ヒラギノ角ゴ Pro W3', Osaka, Verdana, 'MS Pゴシック'; font-size: 16px; line-height: 26px;">も用意します。これは、2つのパラメータを受け取って演算を行い、その結果を返すメソッドを持つ</span><strong style="background-color: white; font-family: Meiryo, メイリオ, ArialMT, 'Hiragino Kaku Gothic Pro', 'ヒラギノ角ゴ Pro W3', Osaka, Verdana, 'MS Pゴシック'; font-size: 16px; line-height: 26px;">Aluクラス</strong><span style="background-color: white; font-family: Meiryo, メイリオ, ArialMT, 'Hiragino Kaku Gothic Pro', 'ヒラギノ角ゴ Pro W3', Osaka, Verdana, 'MS Pゴシック'; font-size: 16px; line-height: 26px;">として実装します。</span><strong style="background-color: white; font-family: Meiryo, メイリオ, ArialMT, 'Hiragino Kaku Gothic Pro', 'ヒラギノ角ゴ Pro W3', Osaka, Verdana, 'MS Pゴシック'; font-size: 16px; line-height: 26px;">ALU</strong><span style="background-color: white; font-family: Meiryo, メイリオ, ArialMT, 'Hiragino Kaku Gothic Pro', 'ヒラギノ角ゴ Pro W3', Osaka, Verdana, 'MS Pゴシック'; font-size: 16px; line-height: 26px;">(Arithmetic Logic Unit)は</span><strong style="background-color: white; font-family: Meiryo, メイリオ, ArialMT, 'Hiragino Kaku Gothic Pro', 'ヒラギノ角ゴ Pro W3', Osaka, Verdana, 'MS Pゴシック'; font-size: 16px; line-height: 26px;">四則演算</strong><span style="background-color: white; font-family: Meiryo, メイリオ, ArialMT, 'Hiragino Kaku Gothic Pro', 'ヒラギノ角ゴ Pro W3', Osaka, Verdana, 'MS Pゴシック'; font-size: 16px; line-height: 26px;">と</span><strong style="background-color: white; font-family: Meiryo, メイリオ, ArialMT, 'Hiragino Kaku Gothic Pro', 'ヒラギノ角ゴ Pro W3', Osaka, Verdana, 'MS Pゴシック'; font-size: 16px; line-height: 26px;">論理演算</strong><span style="background-color: white; font-family: Meiryo, メイリオ, ArialMT, 'Hiragino Kaku Gothic Pro', 'ヒラギノ角ゴ Pro W3', Osaka, Verdana, 'MS Pゴシック'; font-size: 16px; line-height: 26px;">を含むのが一般的ですが、ここではSvm1に必要な加算と乗算のみ用意することにします。</span></blockquote>
そうそう、面倒だから取り敢えず加算と乗算のみやることにしましょう。追加は簡単ですしね。<br />
<br />
<h2 style="text-align: left;">
演算装置</h2>
まあ、これは簡単ですね。オリジナルだとメソッドにしてますが、Schemeでは単純に独立したプロシージャとして実装します。ってか実装って程でもねぇんだけどな。<br />
<br />
<script src="https://gist.github.com/anonymous/aa913cb70bca017fe035.js"></script><br />
<br />
まんまやん、まんま、そのまんまです。<br />
<br />
<h2 style="text-align: left;">
プログラムを仮想スタックマシンへロード</h2>
<div>
さて、<a href="http://www.atmarkit.co.jp/ait/articles/0702/23/news126.html">オリジナル</a>ではバイトデータのファイルでやり取りしてるんですが、どうしましょう。そもそもSchemeだとバイトデータの扱いが良く分からないし(そもそも<a href="http://www.unixuser.org/~euske/doc/r5rs-ja/">仕様</a>にないと思う)、<a href="http://www.atmarkit.co.jp/ait/articles/0702/23/news126.html">オリジナル</a>のバイトデータの仕様も良く分かりません。</div>
<div>
しょーがないんで、プレーンテキストの読み込みに留めておきます。</div>
<div>
そしてそのプレーンテキストへの記述の仕様ですが、</div>
<div>
<ul style="text-align: left;">
<li>計算式は逆ポーランド記法とする</li>
<li><code>bipush</code>命令は続くコードにある値をオペランドスタックに積む(<code>push</code>)</li>
<li><code>iadd</code>命令はオペランドスタックに積まれている値を2つ取り出して(<code>pop</code>)、それぞれの値を加算した結果をオペランドスタックに積む</li>
<li><code>imul</code>命令はオペランドスタックに積まれている値を2つ取り出して(<code>pop</code>)、乗算した結果をオペランドスタックに積む</li>
<li><code>print</code>命令はオペランドスタックの一番上に積まれている値を出力する</li>
</ul>
<div>
とします。</div>
</div>
<div>
具体的には、例えば、1 + 2 * 3の演算命令としては、基本的には逆ポーランド記法なので、1 2 3 * +になるわけですが、テキストファイルへの記述は<br />
<script src="https://gist.github.com/anonymous/049af9c5f31add9b792e.js"></script><br />
<br />
とします。<br />
何だかインチキなアセンブリ言語みたいですが(笑)、取り敢えずこれを良しとして対象としましょう。構造さえ決まってしまえば、あとでどうにでも改造出来ますしね。<br />
<br />
ってなわけで<code>load</code>プロシージャを決まりきったカタチで実装します。<br />
<br />
<script src="https://gist.github.com/anonymous/3013ef4420b4c7aebff9.js"></script><br />
<br />
<h2 style="text-align: left;">
ロードしたプログラムを実行</h2>
</div>
<div>
余談ですが、<a href="http://milkpot.sakura.ne.jp/scheme/r7rs.html">Schemeの最新の仕様書</a>では繰り返し構文が増えています。他の言語ではお馴染みの<code>when</code>(<code>while</code>)や<code>unless</code>が搭載されました。繰り返ししたいけど特に終了時点で返したい値が無い場合重宝しますね。</div>
<div>
ってなわけで<code>unless</code>を使った再帰でプロシージャ<code>execute</code>を実装します。</div>
<div>
<br /></div>
<div>
<script src="https://gist.github.com/anonymous/5c3ef56c41715b5ff9d3.js"></script><br /></div>
<div>
<code>execute</code>は<code>executecommand</code>を呼び出し、<code>executecommand</code>は多値を使って<code>execute</code>との間で<code>code</code>と<code>stack</code>をやり取りします。<code>code</code>は<code>executecommand</code>によって「消費」され、計算の進行に従って<code>stack</code>は(プログラミング用語ではない)状態を変化させていきます。命令を実際に解釈していくのは<code>executecommand</code>です。<br />
では<code>executecommand</code>を実装していきます。<br />
<br />
<h2 style="text-align: left;">
命令の判定を実行</h2>
<blockquote class="tr_bq">
<ul style="text-align: left;">
<li><div style="background-color: white; font-family: Meiryo, メイリオ, ArialMT, 'Hiragino Kaku Gothic Pro', 'ヒラギノ角ゴ Pro W3', Osaka, Verdana, 'MS Pゴシック'; line-height: 26px; margin-bottom: 20px; word-wrap: break-word;">
<code>command</code>が<code>bipush</code>の場合は、続くコードにある値をオペランドスタックへ積む(<code>push</code>)する処理を行います。</div>
</li>
</ul>
</blockquote>
<br />
<blockquote class="tr_bq">
<ul style="text-align: left;">
<li><div style="background-color: white; font-family: Meiryo, メイリオ, ArialMT, 'Hiragino Kaku Gothic Pro', 'ヒラギノ角ゴ Pro W3', Osaka, Verdana, 'MS Pゴシック'; line-height: 26px; margin-bottom: 20px; word-wrap: break-word;">
<code>command</code>が<code>iadd</code>の場合は、オペランドスタックに積まれている値を2つ取り出して(<code>pop</code>)、それぞれの値を加算した結果をオペランドスタックへ積みます。</div>
</li>
</ul>
</blockquote>
<br />
<blockquote class="tr_bq">
<ul style="text-align: left;">
<li><div style="background-color: white; font-family: Meiryo, メイリオ, ArialMT, 'Hiragino Kaku Gothic Pro', 'ヒラギノ角ゴ Pro W3', Osaka, Verdana, 'MS Pゴシック'; line-height: 26px; margin-bottom: 20px; word-wrap: break-word;">
<code>command</code>が<code>imul</code>の場合は、<code>iadd</code>とほぼ同様の処理を行いますが、オペランドスタックへは取り出した値を乗算した結果を積むという点が異なります。</div>
</li>
</ul>
</blockquote>
<br />
<blockquote class="tr_bq">
<ul style="text-align: left;">
<li><div style="background-color: white; font-family: Meiryo, メイリオ, ArialMT, 'Hiragino Kaku Gothic Pro', 'ヒラギノ角ゴ Pro W3', Osaka, Verdana, 'MS Pゴシック'; line-height: 26px; margin-bottom: 20px; word-wrap: break-word;">
<code>command</code>が<code>print</code>の場合は、オペランドスタックの一番上に積まれている値を出力します。</div>
</li>
</ul>
</blockquote>
<br />
これをこのまま実装すると次のようになります。<br />
<br />
<script src="https://gist.github.com/anonymous/c6b30c10a0d30eff5153.js"></script><br />
先ほど書いた通り、<code>executecommand</code>は<code>execute</code>と多値を用いてやり取りします。返り値は<code>code</code>と<code>operandstack</code>の二種類です。<br />
<br />
<h2 style="text-align: left;">
仮想計算機もどきの実行</h2>
<br />
例として、次の3つのファイルを用意しておきます。<br />
<script src="https://gist.github.com/anonymous/f09ba1dc6064e6a730a1.js"></script><br />
<script src="https://gist.github.com/anonymous/56a9998afbf93511ceba.js"></script><br />
<script src="https://gist.github.com/anonymous/bf896c87d2e5e828ea4d.js"></script><br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="http://image.itmedia.co.jp/ait/articles/0702/23/r5compiler03_04.gif" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="http://image.itmedia.co.jp/ait/articles/0702/23/r5compiler03_04.gif" height="130" width="320" /></a></div>
<blockquote class="tr_bq">
<span style="background-color: white; font-family: Meiryo, メイリオ, ArialMT, 'Hiragino Kaku Gothic Pro', 'ヒラギノ角ゴ Pro W3', Osaka, Verdana, 'MS Pゴシック'; font-size: 16px; line-height: 26px;">このプログラムで生成されたSvm1のオブジェクトコードを仮想スタックマシンSvm1で実行します。</span></blockquote>
では、<code>svm1</code>をでっち上げましょう。<br />
<script src="https://gist.github.com/anonymous/9485f4741b34bc56040b.js"></script><br />
<br />
<blockquote class="tr_bq">
<span style="background-color: white; font-family: Meiryo, メイリオ, ArialMT, 'Hiragino Kaku Gothic Pro', 'ヒラギノ角ゴ Pro W3', Osaka, Verdana, 'MS Pゴシック'; font-size: 16px; line-height: 26px;">code0.svmには、「1 + 2」、code1.svmには、「1 + 2 * 3」、code2.svmには、「(1 + 2) * 3」を計算するオブジェクトコードが保存されていますから、実行結果はそれぞれ「3」、「7」、「9」となります。</span></blockquote>
<br />
<script src="https://gist.github.com/anonymous/9fc6ac18ccf52d63f3ae.js"></script><br />
はい、確かになっていますね。<br />
<br />
<h2 style="text-align: left;">
仮想計算機の実行例のイメージ</h2>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="http://image.itmedia.co.jp/ait/articles/0702/23/r5compiler03_05.gif" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="http://image.itmedia.co.jp/ait/articles/0702/23/r5compiler03_05.gif" height="296" width="320" /></a></div>
<br /></div>
<br />
<ol style="text-align: left;">
<li><span style="background-color: white; font-family: Meiryo, メイリオ, ArialMT, 'Hiragino Kaku Gothic Pro', 'ヒラギノ角ゴ Pro W3', Osaka, Verdana, 'MS Pゴシック'; font-size: 16px; line-height: 26px;">最初に、<code>bipush</code>を読み込むと</span><span style="background-color: white; font-family: Meiryo, メイリオ, ArialMT, 'Hiragino Kaku Gothic Pro', 'ヒラギノ角ゴ Pro W3', Osaka, Verdana, 'MS Pゴシック'; font-size: 16px; line-height: 26px;">、次の値を読み込んでオペランドスタックへ「1」を<code>push</code>しています。同様にして、「2」「3」を<code>push</code>します(図では、省略しています)。</span></li>
<li><span style="background-color: white; font-family: Meiryo, メイリオ, ArialMT, 'Hiragino Kaku Gothic Pro', 'ヒラギノ角ゴ Pro W3', Osaka, Verdana, 'MS Pゴシック'; font-size: 16px; line-height: 26px;"><code>imul</code>を読み込むと、オペランドスタックから値を2つ<code>pop</code>して、それらを乗算し、その結果をオペランドスタックへ<code>push</code>します。</span></li>
<li><span style="background-color: white; font-family: Meiryo, メイリオ, ArialMT, 'Hiragino Kaku Gothic Pro', 'ヒラギノ角ゴ Pro W3', Osaka, Verdana, 'MS Pゴシック'; font-size: 16px; line-height: 26px;"><code>iadd</code>を読み込むと、オペランドスタックから値を2つ<code>pop</code>して、それらを加算し、その結果をオペランドスタックへ<code>push</code>します。</span></li>
</ol>
<blockquote class="tr_bq">
<span style="background-color: white; font-family: Meiryo, メイリオ, ArialMT, 'Hiragino Kaku Gothic Pro', 'ヒラギノ角ゴ Pro W3', Osaka, Verdana, 'MS Pゴシック'; font-size: 16px; line-height: 26px;">どうでしょう、スタックを使うと簡単に仮想計算機をソフトウェアで実装することが分かったでしょうか。もちろん、Svm1は仮想計算機というには機能が少な過ぎますが、雰囲気はつかんでいただけたと思います。</span></blockquote>
まあ、雰囲気はつかめましたね、確かに。<br />
<br />
<h2 style="text-align: left;">
今回のソース</h2>
さあて、これ使うと色々な仮想計算機が作れる足がかりになるんでしょうかね。<br />
では今回のソースです。<br />
<br />
<script src="https://gist.github.com/anonymous/0fe873b2b1de0b24f7a4.js"></script><br />
<br /></div>
cametan_001http://www.blogger.com/profile/04882440058326617382noreply@blogger.com1tag:blogger.com,1999:blog-4018043311243103708.post-8278151005228949442014-01-30T08:51:00.000+09:002014-01-30T12:10:15.207+09:00SchemeでBrainfuckインタプリタ<div dir="ltr" style="text-align: left;" trbidi="on">
<div dir="ltr" style="text-align: left;" trbidi="on">
<div class="tr_bq">
さて、Scheme(Racket)でのBrainfuckインタプリタの作成です。</div>
以前にも実は一回書いた事があるってばあるんですが、基本的なエンジンだけの作成で、キチンとしたREPL形式のインタプリタではなかったんですね。<br />
今回はキチンとしたスタンドアローンでREPL構造を持つBrainfuckインタプリタを実装してみたいと思います。<br />
<br />
<h3 style="text-align: left;">
Scheme(<a href="http://www.unixuser.org/~euske/doc/r5rs-ja/">R5RS</a>)と例外処理</h3>
<br />
さて、完全なスタンドアローンのインタプリタを作ろう、って場合、例外処理が欠かせなくなります。何故なら入力が正しく行われなかった場合、システムが落ちてしまって、まあ、みっともないから、ですね。<br />
ところで、Scheme(R5RS)互換でBrainfuckを記述しようとすると、大きな問題に次のような事があり得ます。<br />
Brainfuckの場合、インタプリタと言っても、プロンプトに直接コードを打ち込む事はなく(あっても良いけどちとややこしい事が生じる)、基本的にはコードを記述したテキストファイルを読み込んで解釈実行する、ってスタイルです。つまり、REPLの中で「必ず」ファイルを開く動作を定義しなければなりません。<br />
しかしそこでScheme(R5RS)だとややこしい状態が生じるんですね。「ファイルが存在しない」場合の挙動がイマイチ分からない。<br />
<br />
<blockquote>
<h4 style="text-align: left;">
(with-input-from-file string thunk) 省略可能手続き<br />(with-output-to-file string thunk) 省略可能手続き</h4>
string は,一つのファイルを指名する文字列であること。<br />
そして,thunk は,引数をとらない手続きであること。<br />
with-input-from-file では,ファイルが既存であること。<br />
一方,with-output-to-file では,ファイルが既存だった<br />
ときの効果は未規定である。入力または出力のためにファイ<br />
ルが開かれ,それに接続された入力ポートまたは出力ポー<br />
トが,current-input-port またはcurrent-output-port<br />
によって返される(かつ(read),(write obj ) などで使わ<br />
れる) デフォルト値へと仕立てられ,そしてthunk が無引数<br />
で呼び出される。thunk が戻るときには,ポートが閉じられ<br />
て以前のデフォルトが回復される。with-input-from-file<br />
とwith-output-to-file は,thunk がもたらした(1個ま<br />
たは複数個の) 値を返す。もしも脱出手続きが,これらの手<br />
続きの継続から脱出するために使われるならば,これらの手<br />
続きの振舞は実装依存である。<br />
<h4 style="text-align: left;">
(open-input-file filename) 手続き</h4>
既存のファイルを指名する文字列を取り,そして,そのファ<br />
イルからの文字を配送できる入力ポートを返す。もしもファ<br />
イルを開くことができないならば,エラーが通知される。</blockquote>
つまり、Scheme(R5RS)だと基本的にファイルが存在しない場合はエラーを返し、そいつをまずはトラップすれば良いわけですが、ちとここでSchemeの通知するエラーとは何ぞや、と言う話が出てきます。<br />
<br />
<blockquote>
<h4 style="text-align: left;">
1.3.2. エラー状態と未規定の振舞</h4>
エラー状態について言うとき,この報告書は“エラーが通知<br />
される” という表現を使って,実装がそのエラーを検出し報<br />
告しなければならないことを示す。もしあるエラーの議論に<br />
そのような言い回しが現れないならば,実装がそのエラーを<br />
検出または報告することは,奨励されているけれども,要求<br />
されていない。実装による検出が要求されていないエラー状<br />
態は,普通ただ単に“エラー” と呼ばれる。<br />
たとえば,ある手続きにその手続きで処理することが明示的<br />
に規定されていない引数を渡すことは,たとえそのような定<br />
義域エラーがこの報告書でほとんど言及されていなくても,<br />
エラーである。実装は,そのような引数を含めるように手続<br />
きの定義域を拡張してもよい。<br />
この報告書は“実装制限の違反を報告してもよい” という表<br />
現を使って,実装の課すなんらかの制限のせいで正当なプロ<br />
グラムの実行を続行できない,と報告することが実装に許さ<br />
れている状況を示す。もちろん実装制限は望ましくないが,<br />
実装が実装制限の違反を報告することは奨励されている。<br />
たとえば実装は,あるプログラムを走らせるだけの十分な記<br />
憶領域がないとき,実装制限の違反を報告してもよい。<br />
もしある式の値が“未規定” (unspecified) であると述べられ<br />
ているならば,その式は,エラーが通知されることなく,な<br />
んらかのオブジェクトへと評価されなければならない。しか<br />
し,その値は実装に依存する。この報告書は,どんな値が返<br />
されるべきかを明示的には述べない。</blockquote>
<br />
実はR5RSに載ってる「エラーに関する記述」ってこれだけなんですよ(笑)。あれま。<br />
いや、今まであんま考えて無かったんですが、良く使うエラー提示の"error"って関数も実は存在しなくってあれは実装依存だったんだ、とか今知った所存です(笑)。ダメじゃん(笑)。<br />
つまり、R5RSに関する限り、「エラーを投げる」って事自体が実装依存にならざるを得ないんですね。こう言う事です。<br />
<br />
<br />
<ol style="text-align: left;">
<li>自分で書いたコードに於いてエラーを通知する手段が用意されていない</li>
<li>Schemeシステムが投げるエラーをトラップする手段がない </li>
</ol>
1番に関しては自分で関数を書く(あるいはcall/cc等を使って関数を書く)と言う手段はありますが、2番はとてもややこしいです。<br />
例外処理、と言うのは基本的には「出てきたエラーに対して適切な手段を提示する」わけなんですが、R5RS範疇では「どういうエラーが提示されてるのか」判別する手段が無い。つまり、この辺りは全部Schemeの下のレイヤーで決定されてる、って事になります。<br />
例えばRacketの場合だと、存在しないファイルを開こうとすると、<br />
<br />
<script src="https://gist.github.com/anonymous/8693551.js"></script><br />
<br />
なんて返してくるわけですが、一体これをどうやってトラップするのか?Racketだと<a href="http://docs.racket-lang.org/reference/exns.html#%28def._%28%28lib._racket%2Fprivate%2Fbase..rkt%29._exn%29%29">exn</a>と言う構造体が定義されていて、それを利用してこの場合、<a class="RktValLink" data-pltdoc="x" href="http://docs.racket-lang.org/reference/exns.html#%28def._%28%28lib._racket%2Fprivate%2Fbase..rkt%29._exn~3afail~3afilesystem~3aerrno%29%29" style="background-color: white; color: #0077aa; font-family: 'Source Code Pro', monospace; font-size: 15px; line-height: 24.780000686645508px; margin: 0px; padding: 0px;">exn:fail:filesystem:errno</a>と言うブツを返すらしいんですが、こんなのが他のシステムにも採用されてる保証はない(Pythonみたいに例外クラスをOOPで定義されてる場合も当然あるでしょう)。これは<a href="http://www.chino-js.com/ja/tech/srfi/srfi-34/srfi-34.html">SRFI34</a>、<a href="http://www.chino-js.com/ja/tech/srfi/srfi-35/srfi-35.html">SRFI35</a>、<a href="http://www.chino-js.com/ja/tech/srfi/srfi-36/srfi-36.html">SRFI36</a>とか使っても本質的に解決出来る問題だとは思えないのです。<br />
つまり、この辺がR5RSのポータビリティの限界ですね(笑)。もちろん上のSRFIなんかでも上手くラップしてるのかもしれませんが、エラー定義がシステムの内部依存になってるとすれば、おとなしく(笑)システム依存の例外処理機構使った方が良さそうです。<br />
何か負けた気もするんですが(笑)、今回は諦めて、例外処理に関してはRacketが提供している<a class="RktStxLink Sq" data-pltdoc="x" href="http://docs.racket-lang.org/reference/exns.html#%28form._%28%28lib._racket%2Fprivate%2Fmore-scheme..rkt%29._with-handlers%29%29" style="background-color: white; color: #0077aa; font-family: 'Source Code Pro', monospace; font-size: 15px; margin: 0px; padding: 0px; text-decoration: none;">with-handlers</a>をおとなしく使う事にします(苦笑)。<br />
<br />
<h3 style="text-align: left;">
Brainfuckの仕様</h3>
<br />
まあ、当然知ってる人は知ってるでしょうが、<a href="http://ja.wikipedia.org/wiki/%E3%83%A1%E3%82%A4%E3%83%B3%E3%83%9A%E3%83%BC%E3%82%B8">Wikipedia</a>の方から改めて<a href="http://ja.wikipedia.org/wiki/Brainfuck">Brainfuckの仕様</a>を抜書きしてみます。<br />
<br />
<ol style="background-color: white; font-family: sans-serif; font-size: 15px; line-height: 22.6560001373291px; list-style-image: none; margin: 0.3em 0px 0px 3.2em; padding: 0px;">
<li style="margin-bottom: 0.1em;"><code style="background-color: #f9f9f9; font-family: monospace, Courier;">></code> <a class="mw-redirect" href="http://ja.wikipedia.org/wiki/%E3%83%9D%E3%82%A4%E3%83%B3%E3%82%BF" style="background-image: none; background-position: initial initial; background-repeat: initial initial; color: #0b0080; text-decoration: none;" title="ポインタ">ポインタ</a>を<a href="http://ja.wikipedia.org/wiki/%E3%82%A4%E3%83%B3%E3%82%AF%E3%83%AA%E3%83%A1%E3%83%B3%E3%83%88" style="background-image: none; background-position: initial initial; background-repeat: initial initial; color: #0b0080; text-decoration: none;" title="インクリメント">インクリメント</a>する。ポインタをptrとすると、<a href="http://ja.wikipedia.org/wiki/C%E8%A8%80%E8%AA%9E" style="background-image: none; background-position: initial initial; background-repeat: initial initial; color: #0b0080; text-decoration: none;" title="C言語">C言語</a>の「<code style="background-color: #f9f9f9; font-family: monospace, Courier;">ptr++;</code>」に相当する。</li>
<li style="margin-bottom: 0.1em;"><code style="background-color: #f9f9f9; font-family: monospace, Courier;"><</code> ポインタをデクリメントする。C言語の「<code style="background-color: #f9f9f9; font-family: monospace, Courier;">ptr--;</code>」に相当。</li>
<li style="margin-bottom: 0.1em;"><code style="background-color: #f9f9f9; font-family: monospace, Courier;">+</code> ポインタが指す値をインクリメントする。C言語の「<code style="background-color: #f9f9f9; font-family: monospace, Courier;">(*ptr)++;</code>」に相当。</li>
<li style="margin-bottom: 0.1em;"><code style="background-color: #f9f9f9; font-family: monospace, Courier;">-</code> ポインタが指す値をデクリメントする。C言語の「<code style="background-color: #f9f9f9; font-family: monospace, Courier;">(*ptr)--;</code>」に相当。</li>
<li style="margin-bottom: 0.1em;"><code style="background-color: #f9f9f9; font-family: monospace, Courier;">.</code> ポインタが指す値を出力に書き出す。C言語の「<code style="background-color: #f9f9f9; font-family: monospace, Courier;">putchar(*ptr);</code>」に相当。</li>
<li style="margin-bottom: 0.1em;"><code style="background-color: #f9f9f9; font-family: monospace, Courier;">,</code> 入力から1バイト読み込んで、ポインタが指す先に代入する。C言語の「<code style="background-color: #f9f9f9; font-family: monospace, Courier;">*ptr=getchar();</code>」に相当。</li>
<li style="margin-bottom: 0.1em;"><code style="background-color: #f9f9f9; font-family: monospace, Courier;">[</code> ポインタが指す値が0なら、対応する <code style="background-color: #f9f9f9; font-family: monospace, Courier;">]</code> の直後にジャンプする。C言語の「<code style="background-color: #f9f9f9; font-family: monospace, Courier;">while(*ptr){</code>」に相当。</li>
<li style="margin-bottom: 0.1em;"><code style="background-color: #f9f9f9; font-family: monospace, Courier;">]</code> ポインタが指す値が0でないなら、対応する <code style="background-color: #f9f9f9; font-family: monospace, Courier;">[</code> (の直後<sup class="reference" id="cite_ref-1" style="line-height: 1em; unicode-bidi: -webkit-isolate;"><a href="http://ja.wikipedia.org/wiki/Brainfuck#cite_note-1">[1]</a></sup>)にジャンプする。C言語の「<code style="background-color: #f9f9f9; font-family: monospace, Courier;">}</code>」に相当<sup class="reference" id="cite_ref-2" style="line-height: 1em; unicode-bidi: -webkit-isolate;"><a href="http://ja.wikipedia.org/wiki/Brainfuck#cite_note-2" style="background-image: none; background-position: initial initial; background-repeat: initial initial; color: #0b0080; text-decoration: none; white-space: nowrap;">[2]</a></sup>。</li>
</ol>
<br />
さて、Scheme的に言うと1から6までが「関数」(手続き)そして7番と8番は「特殊形式」(構文、あるいはシンタックス)になりますね。実は1から6までは実装はハナクソなんですが、7と8がややこしい。一体これをどうやってプログラムするのか。<br />
今までParser自体は書いた事があんま無いわけですが、要するにRead部分でBrainfuckのプログラムファイルを読み込んだ時点で、どうやらジャンプ先の候補ってのをマーキングしておいて、それを「パーズと称して」(笑)Evalに渡さなければならないみたいです。この辺はEvalでやる事じゃあなさそうですね。ではやってみますか。
<br />
<br />
<h3 style="text-align: left;">
Read の実装</h3>
<div style="text-align: left;">
まずはファイルを読み込む関数(read-file)を実装していきます。仕様は次の通りとします。</div>
<div style="text-align: left;">
<br /></div>
<div style="text-align: left;">
</div>
<ol style="text-align: left;">
<li>ファイルを開く。</li>
<li>ストリームから一文字ずつ<a href="http://www.schemers.org/Documents/Standards/R5RS/HTML/r5rs-Z-H-9.html#%_idx_614">read-char</a>する。その度にカウンターを+1する。</li>
<li>文字が>、<、+、-、.、,の場合はcodeに<a href="http://www.schemers.org/Documents/Standards/R5RS/HTML/r5rs-Z-H-9.html#%_idx_390">cons</a>する。</li>
<li>文字が[の場合はlsにカウンターの値をpushする。</li>
<li>文字が]の場合はlsの値をpopして、stackにカウンターの値と(car ls)のペア、(car ls)とカウンターの値のペアをpushする(つまり、stackは連想リストとする)。lsに対応する[の位置情報が無い場合、「カッコの数が合致してない」んでエラーを返す。</li>
<li>文字がこの8種類以外の場合は無視してread-charする。</li>
<li>ファイル終端に来た場合、ls内に位置情報が残ってる場合、「カッコの数が合致してない」んでエラーを返す。lsが空リストだったら多値として(<a href="http://www.schemers.org/Documents/Standards/R5RS/HTML/r5rs-Z-H-9.html#%_idx_422">reverse</a> code)と連想リストstackの二値を返す。</li>
</ol>
<div>
この7つでread-fileは完成です。なお、エラーを返すにはRacket実装依存の<a class="RktValLink Sq" data-pltdoc="x" href="http://docs.racket-lang.org/reference/exns.html#%28def._%28%28quote._~23~25kernel%29._raise%29%29" style="background-color: white; color: #0077aa; font-family: 'Source Code Pro', monospace; font-size: 15px; line-height: 24.780000686645508px; margin: 0px; padding: 0px;">raise</a>を使い、stackへのpushには<a href="http://www.chino-js.com/ja/tech/srfi/srfi-1/srfi-1.html">SRFI1</a>のalist-consを用います。</div>
<div>
<br /></div>
<script src="https://gist.github.com/anonymous/8694685.js"></script><br />
<div style="text-align: left;">
<br /></div>
<div style="text-align: left;">
<br /></div>
<div style="text-align: left;">
<br /></div>
<div style="text-align: left;">
さて、read-fileを書き上げたら Read 部本体である parser (実際は parse は read-file がやってるんですが・笑)関数を実装していきます。これは今までのゲームの例のように、インタプリタの現状(phase)に合わせて動作を変更します。次が parser の仕様です。<br />
<br />
<br />
<br />
<ol style="text-align: left;">
<li>関数 parser は x、 phase、 counter、 code、 pointer、 memory、 stackの7つの引数を受け取る関数とする。</li>
<li>parser は phase に従ってその挙動を変えるものとする。なお、phaseの中身は基本的にはシンボルである。</li>
<li>phase が 'read-file だった場合、ユーザーからプロンプトを通じてファイルパスを受け取り、その引数を関数 read-file に受け渡し、返り値を受け取り、多値としてx、 phase、 counter、 code(これは read-file が返したもの)、 pointer、 memory、 stack(これも read-file が返したもの)の7つを返す。</li>
<li>phase が 'read-char だった場合、ユーザーからプロンプトを通じて1文字だけ受け取りそれを新たに x として x、 phase、 counter、 code、 pointer、 memory、 stack の7つの値を多値として返す。</li>
<li>phase が read だった場合、ユーザーからプロンプトを通じて何か受け取り、それを新たに x として x、 phase、 counter、 code、 pointer、 memory、 stack の7つの値を多値として返す。</li>
<li>それ以外の場合は基本的にスルーして、 x、 phase、 counter、 code、 pointer、 memory、 stack の7つの値をそのまま多値として返す。</li>
</ol>
<br />
ゲームの場合もそうですが、「受け取った何らかの入力は」 x に束縛して、それ以外 Read は特に何もやりません。唯一の例外は、 read-file に構文解析させた情報を code と stack に積むのみ、だけですね。あとは phase 情報に従って適切な読み込み系関数を探して実行するだけ、となります。<br />
<br />
<script src="https://gist.github.com/anonymous/8701837.js"></script><br />
<br />
では Eval を見て行きましょう。<br />
<br />
<h3 style="text-align: left;">
Eval の実装</h3>
まず、Eval の実装を始める前に、インタプリタの動作の振る舞いを決定するステージ、 phase がどんなものになるか決めてしまいましょう。<br />
と言うのも、今までのゲームと違って、今回の brainfuck インタプリタの場合、全体的にREPLとしてイベントループを形成するのはもちろんですが、一旦コードが読まれると基本的には Eval 内で繰り返し処理してから Print 部に渡す為、 phase がどういうステージを持つのかハッキリしないとちと混乱してくるからです。<br />
(しかも、Eval 自体でのループの際、 .命令と,命令があった場合、一旦ループを中断して、Print部とRead部の助けを借りないとならない)<br />
<br />
<br />
<ol style="text-align: left;">
<li>introduction ステージ: インタプリタを起動した際にアプリケーションが自己主張するステージ(まあ無くても良いんですが・笑)</li>
<li>read ステージ: 何らかの入力を外部から読み込むステージ</li>
<li>read-file ステージ: Brainfuck プログラムファイルを読み込むステージ</li>
<li>read-char ステージ: 一文字だけ入力を外部から読み込むステージ</li>
<li>put-char ステージ: 一文字だけ表示するステージ</li>
<li>execute ステージ: Brainfuck プログラムを評価するステージ</li>
<li>break ステージ: 何らかのエラーによってプログラムの実行が中止されたステージ</li>
<li>exception ステージ: エラーに関する表示を行うステージ</li>
</ol>
<div>
基本的には上の8つとなります。</div>
<div>
それで、ステージがイベントループの中核になるわけですが、基本的な流れは</div>
<div>
<br /></div>
<blockquote class="tr_bq">
introduction -> read -> read-file -> execute -> read -> read-file -> execute -> ...</blockquote>
<div>
<br /></div>
<div>
と言うカンジで、初回だけ introduction が出てきますが、あとは read -> read-file -> execute -> の繰り返しです。 execute 内でたまに、read-char ステージかあるいは put-char ステージが求められる、と言う考え方です。</div>
<div>
なお、break ステージだけは Eval が設定しません。これは後で例外処理機構が例外を処理した際に設定するステージとしましょう。それを Eval が見て、exception ステージを設定する、と言う考え方で実装します。</div>
<div>
では以下に Eval の簡単な仕様を提示します。</div>
<div>
<br /></div>
<div>
<ol style="text-align: left;">
<li>Eval(interp 関数) は x、 phase、 counter、 code、 pointer、 memory、 stack、の7つの引数を受け取る関数とする。</li>
<li>phase が 'introduction だった場合 phase を 'read に変更し、x、 'read、 counter、 code、 pointer、 memory、 stack、の7つを多値として返す。</li>
<li>phase が 'read だった場合、<ol style="text-align: left;">
<li>x(入力値)がquit、exit、または bye だった場合、brainfuck インタプリタを終了する。</li>
<li>x が load、 open、 または read-file だった場合、phase を 'read-file に変更し、x 'read-file counter code pointer memory stack の7つを多値として返す。</li>
<li>それ以外の場合はスルーして、 x、 phase、 counter、 code、 pointer、 memory、 stack の7つを多値としてそのまま返す。</li>
</ol>
</li>
<li>phase が 'read-file だった場合、BrainfuckコードがRead部から読み込まれた事を意味する。すなわち、phase を 'execute に設定し、再帰的に自身を呼び出す。具体的には x、 'execute、 counter、 code、 pointer、 memory、 stack、を7つの引数として interp 関数を呼び出す。</li>
<li>phase が 'read-char だった場合、pointerが指すメモリの値に 入力値 x を整数に変換した値を足して、phase を 'execute に変更して自身を呼び出す。</li>
<li>phase が 'put-char だった場合、出力が終了した事を意味する。従って、phase を 'excute に変更して自身を呼び出す。</li>
<li>phase が 'execute だった場合、次のように動作する<ol style="text-align: left;">
<li>counterの値がcodeの長さと一致した場合、Brainfuckコードの解釈実行が終了した事を意味する。すなわち初期化として #f、 'read、 0、 #f、 0、 '(0 . 0)、 '()、の7つを多値として返す。</li>
<li>1でなければ counter の指す code の値を解釈実行する。すなわち、Brainfuckの仕様に従って動作する。</li>
</ol>
</li>
<li>phase が 'break だった場合、phase に 'exception を設定して、x、 'exception、 counter、 code、 pointer、 memory、 stack、の7つを多値として返す。</li>
<li>phase が 'exception だった場合、Brainfuckインタプリタシステムがエラー報告をユーザーに行い終わった事を意味する。従って初期化として #f、 'read、 0、 #f、 0、 '(0 . 0)、 '()、の7つを多値として返す。</li>
<li>それ以外の場合は phase に 'introduction を設定して、x、 'introduction、 counter、 code、 pointer、 memory、 stack の7つを多値として返す。</li>
</ol>
<div>
これが基本動作です。<br />
さて、実際はもっと具体的に5-2がどういう事を行うか、なんですが、ちょっと説明しましょう。<br />
基本的には「全く破壊的操作を行わない」Brainfuck仕様を考えています。Wikipediaの説明を見ると、Brainfuckで扱うメモリとは<br />
<br />
<blockquote class="tr_bq">
<span style="background-color: white; font-family: sans-serif; font-size: 15px; line-height: 22.6560001373291px;">少なくとも30000個の要素を持つバイトの</span><a href="http://ja.wikipedia.org/wiki/%E9%85%8D%E5%88%97" style="background-color: white; background-image: none; color: #0b0080; font-family: sans-serif; font-size: 15px; line-height: 22.6560001373291px; text-decoration: none;" title="配列">配列</a><span style="background-color: white; font-family: sans-serif; font-size: 15px; line-height: 22.6560001373291px;">(各要素はゼロで初期化される)</span></blockquote>
となってるんですが、そこはSchemeなんで別に上限値を設定する必要はありません。ではどうするのか。<br />
Schemeには連想リストがあるんで、これをメモリに見立ててみれば良い、と言う事です。<br />
つまり初期値として memory と言う引数には '((0 . 0)) と言う「連想リスト」を与えてます。これは「メモリゼロ番地で、初期値は0」と言う意味合いに対応しています。<br />
>と言う命令を見た時、pointerを1増やすわけですが、その時、平たく言うと memory に '(1 . 0) と言うペアをconsしてやる。そうすれば memory は '((1 . 0) (0 . 0)) と言う状態になって、二つの記憶域を持つ状態になります。もう一回 > があれば今度は memory は '((2 . 0) (1 . 0) (0 . 0)) と言う状態になりますね。<br />
つまり、実質的には > は memoryに (pointer . 0) と言うペアを cons する関数だ、と捉え直す事が出来るのです(もちろん、既にポインタが指すペアが存在する場合は新たにペアを cons する必要はありません)。<br />
<br />
<blockquote class="tr_bq">
注: なお、実際はペアを使って cons するのではなく、SRFI1のalist-consを使って cons していきます。</blockquote>
反面、 < は「既に > が作成したメモリ」をたどっていくだけなので新しいメモリ(のペア)を作成する事はありません。単純に pointer の値を -1 するだけです。<br />
<br />
<blockquote class="tr_bq">
注: なお、本当は上の機構を使えば負の番地を持つメモリも実装可能ですが、今回は敢えてやっていません。pointer の値が負になるとエラーを返します。 </blockquote>
<br />
+、-は pointer の指す memory を <a href="http://www.schemers.org/Documents/Standards/R5RS/HTML/r5rs-Z-H-9.html#%_idx_436">assv</a> して、その <a href="http://www.schemers.org/Documents/Standards/R5RS/HTML/r5rs-Z-H-9.html#%_idx_396">cdr</a> に対して加算(+1)ないしは減算(-1)を行います(これを datum とする)。ただし、ここでも破壊的変更を避ける為、memoryから対象のペアを削除して(この削除は破壊的削除ではない)新しく作ったペアを cons します。<br />
<br />
<blockquote class="tr_bq">
注 : 具体的には、Eval( interp 関数)が引数として memory を保持してるので、再帰過程に於いて SRFI1 の alist-delete を使って対象ペアを除いたカタチで memory を取り出し、それに対して pointer と計算し終わったdatum を alist-cons する。すなわち書式は</blockquote>
<div>
<blockquote class="tr_bq">
(alist-cons pointer datum (alist-delete pointer memory)) </blockquote>
<blockquote class="tr_bq">
となる。 </blockquote>
</div>
</div>
<div>
</div>
</div>
. では一旦 interp 関数のループを止めて、ポインタの指すメモリの値を Print 部に渡さないとなりません。<br />
手順としては、<br />
<br />
<br />
<ol style="text-align: left;">
<li>phase を 'put-char にしてループを抜ける宣言をする。</li>
<li>(<a href="http://www.schemers.org/Documents/Standards/R5RS/HTML/r5rs-Z-H-9.html#%_idx_482">integer->char</a> (cdr (assv pointer memory))) の値を x に束縛して x、 'put-char、 counter + 1、 code、 pointer、 memory、 stack、の7つを多値として返す。</li>
</ol>
<div>
です。</div>
<div>
でこっちは滅多にないんですが、,命令も.に似てますね。</div>
<div>
<br /></div>
<div>
<ol style="text-align: left;">
<li>phase を 'read-char にしてループを抜ける宣言をする。</li>
<li>すなわち、 x、 'read-char、 counter + 1、 code、 pointer、 memory、 stack、の7つを多値として返す。</li>
</ol>
<div>
phase が 'put-char、あるいは 'read-char の場合は既に書きました。基本的には phase に 'executeをセットしなおして自身を再帰呼び出しするだけです。ただし、 read-charモードの場合はメモリに入力値を加算しないといけませんが、前述した通り、SRFI の alist-delete と alist-cons を上手く使って破壊的操作を回避します。<br />
最後はループ命令です。<br />
[を見た時、<br />
<br />
<blockquote class="tr_bq">
<ol>
<li>pointer が指すメモリの値、すなわち (cdr (assv pointer memory)) が0なら stack から counterから飛べる値を探してきて、counter にその値をセットして自身を再帰呼び出しする。 </li>
<li>そうじゃないなら、counterを+1してそのまま処理を継続する。</li>
</ol>
</blockquote>
<br />
具体的な話をすると、 stack も連想リストです。これはRead部でread-fileが解析したジャンプ対象の位置情報が入っています。<br />
例えばBrainfuckの何らかのプログラムの4つ目に[があって、41個目に]があった場合、stackは '((4 . 41) (41. 4))と言うカタチで対応を保持しています。そして実行部である Eval (interp 関数)では、code の4つ目で [ を見た場合 (cdr (assv pointer memory)) の値が 0 であるかどうかをチェックして、そうだった場合、カウンターを一気に41へ(正確には]の後ろなんで42)進めます。<br />
]の場合は、無条件に stack が提示する [ の位置へとカウンターを「戻し」ます。先ほどのケースで、stackが'((4 . 41) (41. 4))だった場合、counterが41なんでcounterを (cdr (assv counter stack)) に従って4に戻して interp 関数は自分を再帰呼び出しします。<br />
<br />
と言うわけで、Eval ( interp 関数)のコードは以下の通りです。<br />
<br />
<script src="https://gist.github.com/anonymous/8698039.js"></script><br />
<br />
<br />
<br />
では最後にPrint部です。<br />
<br />
<h3 style="text-align: left;">
Print の実装</h3>
Print部は相変わらず簡単です。単純に言うとメッセージを大域変数で連想リストとして設定して、phase の条件に従ってassvで検索したペアの cdr を表示すれば良い。そしてその後、返り値を返します。<br />
Print部に直接関連した phase は<br />
<br />
<br />
<ol style="text-align: left;">
<li>introduction</li>
<li>read-char</li>
<li>read-file</li>
<li>put-char</li>
<li>exception</li>
</ol>
<div>
の5つで、そのうち1、2,3は纏められて、4、5も纏められます。</div>
<br />
まずは大域変数 *messages* を設定します。<br />
<br />
<script src="https://gist.github.com/anonymous/8698238.js"></script><br />
<br />
1、2、3のケースだと単純に (cdr (<a href="http://www.schemers.org/Documents/Standards/R5RS/HTML/r5rs-Z-H-9.html#%_idx_434">assq</a> phase *messages*)) を表示すれば良いだけですし(何のために *messages* のキーと phase の値に互換性を持たせてるのか、と言うとこの為です!)、4、5の場合は Eval ( interp 関数)が渡してきた x を表示すれば良い。あとは、プロンプトを表示するだけなんで、Print 部は次のように簡単に書けます。<br />
<br />
<script src="https://gist.github.com/anonymous/237d113d884e08626e7e.js"></script><br />
<br />
これで、BrainfuckのREPL各部位は完成しました。あとは実際REPLとして組み上げるだけです。<br />
<br />
<h3 style="text-align: left;">
REPL と例外処理</h3>
まあ、前からやってる通り、単純にはSchemeのSRFI11を利用して多値関数同士を結んで末尾再帰でREPLを書くだけで良いんですが、各関数(っつーかReadとEval)は場合によっては例外を throw しますが、一方、例外を catch してシステムを安全にする機構は組み上げてません。<br />
平たく言うと、REPL部でそれをやってしまおう、って事です。<br />
まずは、例外処理無しのREPLをひな形として組んでみます。<br />
<br />
<script src="https://gist.github.com/anonymous/49ed6eca00d9b2e8be68.js"></script><br />
<br />
これでも充分動くんですが、例えば「存在しないファイル」を開こうとすると当然エラーが出てシステムは落ちてしまいます。<br />
<br />
<script src="https://gist.github.com/anonymous/8698545.js"></script><br />
<br />
システムが落ちる、とはどういう事か。継続よろしく、エラーと言うのは発生するとトップレベルに強制的に戻される(つまり関数内から抜ける)、って事になるわけですね。<br />
つまり、言い方を変えると、例外処理と言うのはトップレベルに戻る前にそのエラーをまずは「捕まえる」仕組みです。そして捕まえたらそのエラーの種類に従って、適切な処置を施す。<br />
この場合、REPL内でエラーが投げられるわけですが、どういう処理が必要かと言うと、大枠的には「REPL内に戻る」事になります。何故かと言うとインタプリタ内でエラーが起きて別のプログラムが走ったりすればこれは困ります(笑)。インタプリタでエラーが起きたらインタプリタに戻って欲しい。<br />
言い換えると<br />
<br />
<blockquote class="tr_bq">
エラーが起きた -> でもインタプリタ(REPL)に戻る</blockquote>
<br />
ってのが「エラーに対する適切な処置」になるわけです。<br />
基本的にはどんなエラーが起きてもインタプリタに戻って構わないんですが、一応「どんなエラーが起きたか」ユーザーに示さないといけない(利便性の問題です)。その為に「どんなエラーが起きたのか」知る必要があって、その為のエラートラップになるわけですね。<br />
今まで組み上げたプログラムを見ると(想定された)エラーの種類、ってのは次の通りです。<br />
<br />
<br />
<ol style="text-align: left;">
<li>read-file 内で起きた「ファイルが見つかりません」エラー</li>
<li>read-file 内で起きた「カッコの数が合いません」エラー</li>
<li>eval内で起きた「pointerの値が負の数です」エラー</li>
</ol>
<div>
この3つが想定されてるエラーです。1番はScheme(より正確に言うとRacket)組み込みのエラー、2番と3番はプログラマ側が決めたエラーです。</div>
<div>
でこれを実装する為にRacketの<span style="background-color: white; font-family: Charter, serif; font-size: 18px; line-height: 24.780000686645508px;"> </span><span class="RktSym" style="background-color: white; font-family: 'Source Code Pro', monospace; font-size: 1rem; line-height: 24.780000686645508px; margin: 0px; padding: 0px;"><a class="RktStxLink Sq" data-pltdoc="x" href="http://docs.racket-lang.org/reference/exns.html#%28form._%28%28lib._racket%2Fprivate%2Fmore-scheme..rkt%29._with-handlers%29%29" style="color: #0077aa; font-size: 1rem; margin: 0px; padding: 0px;">with-handlers</a> </span>を用います。<span style="background-color: white; font-family: Charter, serif; font-size: 18px; line-height: 24.780000686645508px;"> </span><span class="RktSym" style="background-color: white; font-size: 1rem; line-height: 24.780000686645508px; margin: 0px; padding: 0px;"><a class="RktStxLink Sq" data-pltdoc="x" href="http://docs.racket-lang.org/reference/exns.html#%28form._%28%28lib._racket%2Fprivate%2Fmore-scheme..rkt%29._with-handlers%29%29" style="color: #0077aa; font-family: 'Source Code Pro', monospace; font-size: 1rem; margin: 0px; padding: 0px;">with-handlers</a></span>の構文は次のようになっています。</div>
<div>
<span class="RktSym" style="background-color: white; font-family: 'Source Code Pro', monospace; font-size: 1rem; line-height: 24.780000686645508px; margin: 0px; padding: 0px;"><br /></span></div>
<blockquote class="tr_bq">
<span class="RktSym" style="background-color: white; font-family: 'Source Code Pro', monospace; font-size: 1rem; line-height: 24.780000686645508px; margin: 0px; padding: 0px;">(</span><span style="background-color: white; font-family: Charter, serif; font-size: 18px; line-height: 24.780000686645508px;"> </span><span class="RktSym" style="background-color: white; font-family: 'Source Code Pro', monospace; font-size: 1rem; line-height: 24.780000686645508px; margin: 0px; padding: 0px;"><a class="RktStxLink Sq" data-pltdoc="x" href="http://docs.racket-lang.org/reference/exns.html#%28form._%28%28lib._racket%2Fprivate%2Fmore-scheme..rkt%29._with-handlers%29%29" style="color: #0077aa; font-size: 1rem; margin: 0px; padding: 0px;">with-handlers</a> ((述語 エラー処理) ...)</span><span style="font-family: Source Code Pro, monospace;"><span style="background-color: white; line-height: 24.780000686645508px;"> (本体))</span></span></blockquote>
<div>
つまり、まず構文的には、(let-values ... で形成されたREPLの本体を(with-handlers で包んでやれば良い、って事になる。</div>
<div>
加えると、述語なんですが、要するにこれがエラー種類を判定する述語です。</div>
<div>
先にも書きましたが、1. のケースだとRacket自体が「どういう種類のエラーなのか」定義してる部分で、これはプログラマ側でどーにか変更する、ってモノじゃありません。リファレンスで調べなきゃいけない範疇となります(笑)。で、<a class="RktValLink" data-pltdoc="x" href="http://docs.racket-lang.org/reference/exns.html#%28def._%28%28lib._racket%2Fprivate%2Fbase..rkt%29._exn~3afail~3afilesystem~3aerrno%29%29" style="background-color: white; color: #0077aa; font-family: 'Source Code Pro', monospace; font-size: 15px; line-height: 24.780000686645508px; margin: 0px; padding: 0px; text-decoration: none;">exn:fail:filesystem:errno</a> <span style="font-family: inherit;">ってのがそのエラーの型で</span> <a class="RktValLink" data-pltdoc="x" href="http://docs.racket-lang.org/reference/exns.html#%28def._%28%28lib._racket%2Fprivate%2Fbase..rkt%29._exn~3afail~3afilesystem~3aerrno%29%29" style="background-color: white; color: #0077aa; font-family: 'Source Code Pro', monospace; font-size: 15px; line-height: 24.780000686645508px; margin: 0px; padding: 0px; text-decoration: none;">exn:fail:filesystem:errno</a>?<span style="font-family: inherit;"> がその述語になります(ちなみに exn 自体は変数扱いになるようです)。</span></div>
<div>
2番と3番がプログラマ側が定義したエラーで、<a class="RktValLink Sq" data-pltdoc="x" href="http://docs.racket-lang.org/reference/exns.html#%28def._%28%28quote._~23~25kernel%29._raise%29%29" style="background-color: white; color: #0077aa; font-family: 'Source Code Pro', monospace; font-size: 15px; line-height: 24.780000686645508px; margin: 0px; padding: 0px; text-decoration: none;">raise</a> で定義した・・・事実上、定義したエラーになります。何を定義したのか、と言うと <a class="RktValLink Sq" data-pltdoc="x" href="http://docs.racket-lang.org/reference/exns.html#%28def._%28%28quote._~23~25kernel%29._raise%29%29" style="background-color: white; color: #0077aa; font-family: 'Source Code Pro', monospace; font-size: 15px; line-height: 24.780000686645508px; margin: 0px; padding: 0px; text-decoration: none;">raise</a> に与えた引数がその型を定義してる、って言って過言じゃないです。何故 read-file や interp で使った <a class="RktValLink Sq" data-pltdoc="x" href="http://docs.racket-lang.org/reference/exns.html#%28def._%28%28quote._~23~25kernel%29._raise%29%29" style="background-color: white; color: #0077aa; font-family: 'Source Code Pro', monospace; font-size: 15px; line-height: 24.780000686645508px; margin: 0px; padding: 0px; text-decoration: none;">raise</a> にシンボルを引数として与えたのか、と言う理由がここにあります。</div>
<div>
と言うのも、シンボルを引数にして与えた <a class="RktValLink Sq" data-pltdoc="x" href="http://docs.racket-lang.org/reference/exns.html#%28def._%28%28quote._~23~25kernel%29._raise%29%29" style="background-color: white; color: #0077aa; font-family: 'Source Code Pro', monospace; font-size: 15px; line-height: 24.780000686645508px; margin: 0px; padding: 0px; text-decoration: none;">raise</a> によるユーザー定義のエラーの場合、述語は単純にラムダ式となって</div>
<div>
<br /></div>
<blockquote class="tr_bq">
(lambda (v) (eq? '何かのシンボル v))</blockquote>
<div>
<br /></div>
<div>
と書ける。一般に<b>Schemeを含むLisp族はシンボルはユニーク(単一しか存在し得ない)である事が保証されていて、しかも eq? による比較は高速で決着が付く</b>。これを考えると <a class="RktValLink Sq" data-pltdoc="x" href="http://docs.racket-lang.org/reference/exns.html#%28def._%28%28quote._~23~25kernel%29._raise%29%29" style="background-color: white; color: #0077aa; font-family: 'Source Code Pro', monospace; font-size: 15px; line-height: 24.780000686645508px; margin: 0px; padding: 0px; text-decoration: none;">raise</a> する場合は圧倒的にシンボルを引数に与えた方が得だ、って事になりますね。</div>
<div>
そして、<span style="background-color: white; font-family: Charter, serif; font-size: 18px; line-height: 24.780000686645508px;"> </span><span class="RktSym" style="background-color: white; font-family: 'Source Code Pro', monospace; font-size: 1rem; line-height: 24.780000686645508px; margin: 0px; padding: 0px;"><b><a class="RktStxLink Sq" data-pltdoc="x" href="http://docs.racket-lang.org/reference/exns.html#%28form._%28%28lib._racket%2Fprivate%2Fmore-scheme..rkt%29._with-handlers%29%29" style="color: #0077aa; font-size: 1rem; margin: 0px; padding: 0px;">with-handlers</a> </b></span>のエラー処理もラムダ式で記述します。<br />
気を付けないといけないのは、この場合のラムダ式の引数は述語部分の引数(あるいは変数)と同じじゃないといけません。</div>
</div>
</div>
</div>
</div>
<div>
つまり、組み込みエラーの場合は ext がシンボルなんでエラー処理部分のラムダ式の引数は ext 、他の場合は自分で決めたもの、例えば述語部位が v を使ってたらエラー処理部分のラムダ式の引数も v にする、って事ですね。</div>
<div>
さて、さっきも書いた通り、エラー処理自体は「REPLに戻るのが前提」と書きました。ここで整理すると、</div>
<div>
<span style="font-family: Source Code Pro, monospace;"><span style="background-color: white; line-height: 24.780000686645508px;"><br /></span></span></div>
<div>
<ol style="text-align: left;">
<li><span style="font-family: Source Code Pro, monospace;"><span style="line-height: 24.780000686645508px;">x はPrint部の為に「こう言うエラーが起きました」と言う文字列情報を入れる。</span></span></li>
<li><span style="font-family: Source Code Pro, monospace;"><span style="line-height: 24.780000686645508px;">phase は 'break にセットする。</span></span></li>
<li><span style="font-family: Source Code Pro, monospace;"><span style="line-height: 24.780000686645508px;">あとの引数は(どの道 Eval が初期化する前提なんで)手を付けない。</span></span></li>
<li><span style="font-family: Source Code Pro, monospace;"><span style="line-height: 24.780000686645508px;">以上の情報を持ってREPLを再度呼び出す</span></span></li>
</ol>
<div>
<span style="font-family: Source Code Pro, monospace;"><span style="line-height: 24.780000686645508px;">となります。つまり、エラー処理のラムダ式は原則</span></span></div>
</div>
<div>
<span style="font-family: Source Code Pro, monospace;"><span style="line-height: 24.780000686645508px;"><br /></span></span></div>
<blockquote class="tr_bq">
<span style="font-family: Source Code Pro, monospace;"><span style="line-height: 24.780000686645508px;">(lambda (引数) (repl エラー情報文字列 'break counter code pointer memory stack))</span></span></blockquote>
<br />
になる、って事を意味します。<br />
これを考慮してREPLを修正します。<br />
<br />
<script src="https://gist.github.com/anonymous/8699197.js"></script><br />
<br />
これで、仮に「存在しないファイル」をREPLで開いた場合でも、Brainfuckインタプリタは落ちないで、そのまま処理を継続します。<br />
<br />
<script src="https://gist.github.com/anonymous/8699265.js"></script><br />
<br />
以上でBrainfuckインタプリタの完成です。以下に全コードを載せておきます。<br />
<br />
<script src="https://gist.github.com/anonymous/8699291.js"></script><br />
<br />
<br />
<br />
<h3 style="text-align: left;">
Racket で実行形式作成</h3>
以前から Racket には <a href="http://docs.racket-lang.org/raco/index.html">raco</a> と言うコンパイラが付属してたんですが、コマンドラインツールだし、何かメンドくせえし(笑)、とかでスタンドアローンでの実行形式作るのは諦めてたんですね。<br />
しかし最近、とうとう DrRacket IDEからメニューから選んで実行形式が作れるようになる、と大進歩を遂げました。Lisp系言語だと「スタンドアローンは作りづらい」って言われてたんですが、ここに来てやっとLispプログラムの恩恵を一般に配布しやすくなった、って言って良いでしょう。<br />
ただ、残念なのは、どうもユニコードの問題があって、日本語なんかの文字列が入ってるとコンパイラが通ってもすぐ落ちたりしちゃうんですね(今回、メッセージ表記を全て英語にしたのはそのせいです)。<br />
<br />
ファイルを保存してからDr.Racketの [Racket] メニューから [実行ファイルの作成...]を選びます。<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjK6C-ZqhKgn8nphV_L1tOCO8YHoxDrN0meU2in6GDzM-iRIlF1q6-ZNkwpxTqFqUMVqNRjAERiWGVBS9h0bwP4dpE14j7CiGYsF7nCnkMTByowF-rfGU1AXasw-_taJIDza5TsAxWDKskW/s1600/racket.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjK6C-ZqhKgn8nphV_L1tOCO8YHoxDrN0meU2in6GDzM-iRIlF1q6-ZNkwpxTqFqUMVqNRjAERiWGVBS9h0bwP4dpE14j7CiGYsF7nCnkMTByowF-rfGU1AXasw-_taJIDza5TsAxWDKskW/s1600/racket.png" height="294" width="320" /></a></div>
<br />
そうするとポップアップメニューが現れます。<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgZ7n0GhId08aF18QzQcu2TNmpv5Rf-a6QJ9g8XIPGvWHrx0i6eBpg-gIOIf6LMYrI-rJYA3Kqsdpuny22nT6QZa5RIF2fND-0FtIYKolhW7KkPJXCjIsSxqt4CwamdCQbUNOmGHIBXDxQx/s1600/racket2.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgZ7n0GhId08aF18QzQcu2TNmpv5Rf-a6QJ9g8XIPGvWHrx0i6eBpg-gIOIf6LMYrI-rJYA3Kqsdpuny22nT6QZa5RIF2fND-0FtIYKolhW7KkPJXCjIsSxqt4CwamdCQbUNOmGHIBXDxQx/s1600/racket2.png" height="146" width="320" /></a></div>
<br />
<b>Type</b>に関しては一番下の<b>Distribution</b>を使えば良いでしょう。と言うのも、これがアプリの配布形式を作るブツだからです。<br />
<br />
<blockquote class="tr_bq">
注: 一番目のブツは平たく言うとエイリアスを作るだけで、コンパイルして実行形式を作るとはいえない。二番目はコンパイルは行うが、その実行にはRacket本体が必要で、要するにいわゆる「ランタイム」を含めた実行形式を作成するには3番目の<b>Distribution</b>しかなく、配布目的、あるいは完全なスタンドアローンを作るならこれを利用するしかなくなる。</blockquote>
<br />
BaseもRacketを使えば良いです(GRacketはRacket(コマンドライン版)のちょっとしたGUI版程度です)。<br />
あとは [作成] ボタンを押せば自動的に実行形式を含むzipファイルを作成してくれます(圧縮までしてくれるたぁ何て親切・笑!)<br />
<br />
<h3 style="text-align: left;">
Brainfuck の大きさ</h3>
Wikipediaによると<br />
<br />
<blockquote class="tr_bq">
<span style="background-color: white; font-family: sans-serif; font-size: 15px; line-height: 22.6560001373291px;">開発者Urban Müllerが</span><a href="http://ja.wikipedia.org/wiki/%E3%82%B3%E3%83%B3%E3%83%91%E3%82%A4%E3%83%A9" style="background-color: white; background-image: none; color: #0b0080; font-family: sans-serif; font-size: 15px; line-height: 22.6560001373291px; text-decoration: none;" title="コンパイラ">コンパイラ</a><span style="background-color: white; font-family: sans-serif; font-size: 15px; line-height: 22.6560001373291px;">がなるべく小さくなる言語として考案した。 実際、Müllerが開発したコンパイラのサイズはわずか123</span><a href="http://ja.wikipedia.org/wiki/%E3%83%90%E3%82%A4%E3%83%88_(%E6%83%85%E5%A0%B1)" style="background-color: white; background-image: none; color: #0b0080; font-family: sans-serif; font-size: 15px; line-height: 22.6560001373291px; text-decoration: none;" title="バイト (情報)">バイト</a><span style="background-color: white; font-family: sans-serif; font-size: 15px; line-height: 22.6560001373291px;">、</span><a href="http://ja.wikipedia.org/wiki/%E3%82%A4%E3%83%B3%E3%82%BF%E3%83%97%E3%83%AA%E3%82%BF" style="background-color: white; background-image: none; color: #0b0080; font-family: sans-serif; font-size: 15px; line-height: 22.6560001373291px; text-decoration: none;" title="インタプリタ">インタプリタ</a><span style="background-color: white; font-family: sans-serif; font-size: 15px; line-height: 22.6560001373291px;">は98バイトであった。</span></blockquote>
なんだそうなんですが、Schemeで書いたBrainfuckインタプリタの実行形式は何と<b>5.09メガバイト</b>もありました(笑)。何と<b>53倍弱の大きさで</b>す(笑)。いやあ、富豪の時代ですねぇ(笑)。<br />
これ、twitterでも笑って言ってたんですが(笑)。5.09メガバイトって事はフロッピーディスク一枚に収まらないんですよ(笑)。何枚くらいになるんだろ・・・まあフロッピー四枚くらいで、90年代前半だとゲームで多くてフロッピー二枚、四枚なんて殆どOS並の「大きさ」なんです。<br />
これやってみたらやっぱ長い間Lisp系言語が人気無かったのは分かりますね。Brainfuckくらいの単純なインタプリタでも書いちゃうとOS並の大きさになっちゃう、ってのは20年くらい前のPCだとマジで死活問題です(笑)。いやあ、今の時代は良い時代ですねぇ(笑)。<br />
<br />
ってなわけで、実行形式へのリンクも貼っておきますね。<br />
<br />
<iframe frameborder="0" height="120" scrolling="no" src="https://skydrive.live.com/embed?cid=C5297123E0B737C0&resid=C5297123E0B737C0%21184&authkey=AF5YlnCJj9jZ0PI" width="98"></iframe><br /></div>cametan_001http://www.blogger.com/profile/04882440058326617382noreply@blogger.com0tag:blogger.com,1999:blog-4018043311243103708.post-47472147363768842352014-01-27T11:36:00.000+09:002014-01-29T14:04:20.728+09:00REPL VS. MVC<div dir="ltr" style="text-align: left;" trbidi="on">
さて、次のような<a href="http://wxpython.org/">wxPython</a>を使った記事があります。<br />
<br />
<a href="http://wiki.wxpython.org/ModelViewController">ModelViewController</a><br />
<br />
これは<a href="http://wiki.wxpython.org/WxLibPubSub">wx.lib.pubsub</a>と呼ばれるライブラリを使ってMVCパターンでGUIアプリを作る例なんですが、かなり錯綜してるように見えますね。<br />
基本的には<br />
<br />
<br />
<ol style="text-align: left;">
<li>Modelが<a href="http://www.wxpython.org/docs/api/wx.lib.pubsub.PublisherClass-class.html#sendMessage">sendMessage</a>メソッドを用いてインスタンス変数を(相手が誰か分からないにせよ)メッセージとして送信する。</li>
<li>ControllerがView(あるいはフレーム)のインスタンスを保持してる。</li>
<li>Controllerが<a href="http://www.wxpython.org/docs/api/wx.lib.pubsub.PublisherClass-class.html#subscribe">subscribe</a>メソッドを用いてメッセージを受信する。</li>
<li>ControllerがViewをチェンジする。</li>
</ol>
<div>
となってて、Controllerの仕事が多すぎますし、全てがControllerに依存しています。</div>
<div>
これが「望むべくMVCの書き方」なのか、って言われると、理論的には「?」なんじゃないんでしょうか。ちょっと釈然としませんね。</div>
<div>
ところがこれが、クラス同士の通信、って概念で考えると、結構既存のGUIフレームワークとMVCの相性って良くねぇんじゃねえの、とか色々試行錯誤した結果思いました。REPLだとreturnで結んだRead、Eval、Print同士が仲良く、ある種イベントループを作り上げるんですが、現状のGUIフレームワークだと、恐らくそのルーツはVisual Basicらしいんですが、要するにフレームに「各種メソッドが個別にぶら下がってる」モデルがベースな為、実はREPL構造とは相性が悪い。悪いくせにMVCなんてやると結構大変ですね。</div>
<div>
本当はMVCパターンを狙うならMVCに適したGUIフレームワークが必要なのかもしれません。ハッキリ言えばREPLを写像する、ってアイディアから言うと<b>GUIフレームワークが用意してるイベントループが邪魔</b>なんですよね。REPLだと自分でイベントループを簡単に書けちゃうし、またクラス同士の独立性も高まります。ひょっとしたら低レベルツールを使えば何とかなるのかもしれませんが、いずれにせよ、恐らく、現代の多くのGUIフレームワークは元々「個別のイベントとそれが関与してるメソッド」を「ぶら下げる」為に本体自体がイベントループを用意せざるを得なかったんでしょう。</div>
<div>
<br /></div>
<div>
ここんとこやってたのは<b>REPLのMVCへの写像</b>がテーマです。つまり、キチンとしたREPLを持つCLIのアプリケーションを書いて、その<b>CLIアプリケーションを最小限の努力でGUI化する</b>にはどうすれば良いかを考えてました。なかなかこれが苦戦してたんですが、ある程度の方針が分かりました。それは次のようなモノです。</div>
<div>
<br /></div>
<div>
<ol style="text-align: left;">
<li>ViewにはControllerのインスタンスを埋め込む。</li>
<li>ControllerにはModelのインスタンスを埋め込む。</li>
<li>Modelは返り値を返す(returnする)。</li>
<li>ViewはControllerのインスタンス.メソッドを引数を付けて呼び出して、その返り値を利用して何かしら表示する。</li>
<li>GUIフレームワークのイベントループにはViewのインスタンスを渡す。</li>
<li>環境情報等は極論Viewが保持する(もちろん環境クラスを作っても良いが、そのインスタンスはViewで管理する)。</li>
</ol>
<div>
の6つですね。これなら比較的REPLの構造を崩さないで済みます。</div>
</div>
<div>
ただ、Viewに環境等の情報を埋め込む限り、<b>破壊的操作は避けられない</b>と言うデメリットが生じますが、これは現行のGUIフレームワークを用いる以上、しょうがないでしょう。</div>
<div>
では、実際に前出のwxPythonを用いたMVCの例に従って、GUIで(大した事はないですが)アプリケーションを組んでいきましょう。</div>
<div>
<br /></div>
<h3 style="text-align: left;">
CLIのアプリケーションを作る</h3>
<div>
<br /></div>
<div>
まずは、上記のアプリと同じように動くCLIのアプリケーションをREPLモデルで組み上げます。まあ、アプリケーションって程大したプログラムでもないんですが、一方、MVCにどうやってコンバートするのか、と言うのを見るには単純な方が良いんで、まあいいでしょう。</div>
<div>
次のコードがCLI版になります。</div>
<div>
<br /></div>
<div>
<script src="https://gist.github.com/anonymous/8638396.js"></script><br /></div>
<div>
<br /></div>
<div>
<br /></div>
<div>
<br /></div>
<div>
ホント大した事がないんですが、それでも重要な事があります。それはこれです。<br />
<br />
<blockquote class="tr_bq">
Pythonの場合、継承目的で作成してメソッドを全部子クラスに継承させたい場合、プライベートメソッド(アンダーバー二つ__で始まるアレ)は作らない</blockquote>
<br />
です。これ、どういう理由なんだか知らないんですが、プライベートメソッドにしちゃうと、Pythonだと子クラスに継承出来ないんですね。色々Pythonに関して調べてみたんですが、どういう理由でこうなってるのか分かりません。<br />
<br />
<blockquote class="tr_bq">
注: Twitterで教えてもらったんですが、通常、OOPな言語の場合(例えばC#)、「他から呼び出されたくなくても、子クラスに継承したい場合のメソッドではプライベートじゃなくってProtectedメソッドにする模様です。 </blockquote>
<br />
まあそれさえ気を付ければ良い、って事ですね。<br />
ではこれをひな形にして、MVCによるGUI版を作っていきます。<br />
<br />
<h3 style="text-align: left;">
Qt Designer</h3>
原版はwxWidgetで作られていたんですが、今回は<a href="https://pypi.python.org/pypi/PySide">PySide</a>と言う<a href="http://qt-project.org/">Qt</a>のPythonバインディングを用います。Qt知らない人はあんまいないと思うんですが、一応説明すると、Googleのアプリケーションなんかでも用いられているC++で書かれたマルチプラットフォームのGUIフレームワークで、他にはLinuxの<a href="http://www.kde.org/">KDE</a>を作ってるツールって事で有名ですね。wxWidgetなんかに比べても高機能だと言われています。<br />
Windowsの場合、PySideをインストールすると、C:\Python27\Lib\site-packages\PySide\にdesigner.exe、つまり<a href="http://qt-project.org/doc/qt-4.8/designer-manual.html">Qt Designer</a>と言うGUI Builderが入ってますんで、これ使ってサクサクとまずはFrameを作っていきます。<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhC1r4aznyP9Eei8p6uNdwI-vDYKdemsOoxmQizWq0bOE5IzNHSv4pZ2tdu5Pm0IzticBXam7N688b2pWquOAfWlWvp9Qi483sW2iw3di7IVhN7GPe0ZEKLr6qr7Fz5ekfDWdrxwKolfdvw/s1600/qtdesigner.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhC1r4aznyP9Eei8p6uNdwI-vDYKdemsOoxmQizWq0bOE5IzNHSv4pZ2tdu5Pm0IzticBXam7N688b2pWquOAfWlWvp9Qi483sW2iw3di7IVhN7GPe0ZEKLr6qr7Fz5ekfDWdrxwKolfdvw/s1600/qtdesigner.png" height="169" width="320" /></a></div>
<br />
Qt Desingerを起動すると次のようなポップアップが現れます。<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjKJL03fOe-l6k9ts64dpLW5Ml5gPkGBBWygQfDvrQbaSm48pMufNNaJOHOJDDFGoChaCiJV6TQCB7v6vkVvMqOOv_I_7wdY0B0pmfiCki9364X7BLHc_GgkavkWI9OlqtXn6PheqR1Amsv/s1600/dialog1.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjKJL03fOe-l6k9ts64dpLW5Ml5gPkGBBWygQfDvrQbaSm48pMufNNaJOHOJDDFGoChaCiJV6TQCB7v6vkVvMqOOv_I_7wdY0B0pmfiCki9364X7BLHc_GgkavkWI9OlqtXn6PheqR1Amsv/s1600/dialog1.png" height="292" width="320" /></a></div>
<br />
<b>Dialog without Buttons</b>を選んで[Create]ボタンを押します。<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiIZl78vAXb1j5W9rsDB6JJr1HeTbQTsPzkYQG6xA3SjPwQolWllT_qfjPsGYyxOwNNuCVI7_3vQs2_n6-QbrTbzGhQdm8iibv5OUfP0dE0-k8sOCJZxMzTOyW7s6jTC94cQxQyNizFgc8R/s1600/qtdesigner2.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiIZl78vAXb1j5W9rsDB6JJr1HeTbQTsPzkYQG6xA3SjPwQolWllT_qfjPsGYyxOwNNuCVI7_3vQs2_n6-QbrTbzGhQdm8iibv5OUfP0dE0-k8sOCJZxMzTOyW7s6jTC94cQxQyNizFgc8R/s1600/qtdesigner2.png" height="170" width="320" /></a></div>
<br />
左側にある<b>Widget Box</b>から、<b>Label</b>、<b>LCDNumber</b>、<b>Button</b>二つをドラックアンドドロップで持ってきて、適当に次のようにDialog上に配置します。<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEipOUOkMzSlEqdW1XG4UpxLUW4kBIDF7xz9CNJq24xx6Xy9gN9b3L4Iilv4AZ9YQeTQ4gK2qGSatZ4P89pUCB0Tz0aJaKlDA3VxZFYjtYgJPZpcAbZAjmZT8dxGUZ4136A_7jX_ApP4Wdeu/s1600/qtdesighner3.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEipOUOkMzSlEqdW1XG4UpxLUW4kBIDF7xz9CNJq24xx6Xy9gN9b3L4Iilv4AZ9YQeTQ4gK2qGSatZ4P89pUCB0Tz0aJaKlDA3VxZFYjtYgJPZpcAbZAjmZT8dxGUZ4136A_7jX_ApP4Wdeu/s1600/qtdesighner3.png" height="169" width="320" /></a></div>
<br />
んで、レイアウト調整なんですが、上のツールバーにこの手のボタンが並んでるんですが。<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi7KPeBRNcBxyrb7p5l-wVCIO6AtLvMvhgXtIpFCZUYy5vK_Sj4lOIGN5Qa9n1Km0eOybWklTGRVPD9n_QydXKf3TVsQxahHJGvcR5xQ4YxDycWtgY9WKJwj7HZE47D5De3tzu8nmXSZ33K/s1600/toolbar.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi7KPeBRNcBxyrb7p5l-wVCIO6AtLvMvhgXtIpFCZUYy5vK_Sj4lOIGN5Qa9n1Km0eOybWklTGRVPD9n_QydXKf3TVsQxahHJGvcR5xQ4YxDycWtgY9WKJwj7HZE47D5De3tzu8nmXSZ33K/s1600/toolbar.png" /></a></div>
<br />
例えばボタン二個をCtrl押しながらマウスで選択して、一番左のボタン(Lay Out Horizontally)とか押すと、ボタン二個がキチンと並んで、かつその状態(ボタン二個が入った大枠)を引き伸ばしたりして大きさ調整出来るんですね。<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjqFLPG5pGv-5aRJ62TVJDd-9TtsK6v-gW9rS_3SBswF1WUeKqGEieG1TD-2sikK4C5vONR812_XBL-vDqN1_s2cvGqgWQfqNlV0BO0248aIEUlNk2co_MPYVLaIA_3bElZVJy3HsAtNp7o/s1600/qtdesigner4.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjqFLPG5pGv-5aRJ62TVJDd-9TtsK6v-gW9rS_3SBswF1WUeKqGEieG1TD-2sikK4C5vONR812_XBL-vDqN1_s2cvGqgWQfqNlV0BO0248aIEUlNk2co_MPYVLaIA_3bElZVJy3HsAtNp7o/s1600/qtdesigner4.png" height="169" width="320" /></a></div>
<br />
同様に、Label、LCDNumber、そしてボタンの大枠を全部選択してLay Out in a Gridボタンを押すと次のようになって、また大枠を引き伸ばせます。<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEguQbUGyieKfYQ75yrG16Y_lNLEkZhwPcYYwXFDWafLKFr6uq0OCMxtTErTFW2GRTKRszEtaF9kFiPf8Epx42XPVOHysx6nu1sXwwVJjP0FTFqAwW7xONnQdSJZ4D9cOcNBjQtbXxAUdadf/s1600/qtdesigner6.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEguQbUGyieKfYQ75yrG16Y_lNLEkZhwPcYYwXFDWafLKFr6uq0OCMxtTErTFW2GRTKRszEtaF9kFiPf8Epx42XPVOHysx6nu1sXwwVJjP0FTFqAwW7xONnQdSJZ4D9cOcNBjQtbXxAUdadf/s1600/qtdesigner6.png" height="169" width="320" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh3hiXtaU02sC0Ix-pYuLvxpgAKNbkEtBnSqOPkVdZQoRt0CQ2hzZXfBnjuRWHdq8xv-aKYp9CY8JxlDD2cwR-bFVJRFx9_e1ogY3Cki8W9gV9eELuyt6xHtoNtDz5_b8CVNLw61Wn33Dwz/s1600/qtdesigner7.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh3hiXtaU02sC0Ix-pYuLvxpgAKNbkEtBnSqOPkVdZQoRt0CQ2hzZXfBnjuRWHdq8xv-aKYp9CY8JxlDD2cwR-bFVJRFx9_e1ogY3Cki8W9gV9eELuyt6xHtoNtDz5_b8CVNLw61Wn33Dwz/s1600/qtdesigner7.png" height="169" width="320" /></a></div>
<br />
<b>Form</b>プルダウンメニューから[Preview...]を選べば、今の状態が実際にどんなもんなのか見る事が出来ます。<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEigZhVEY5QJWaxHiPDmCZOYVOBTAVtkKuOn2eI5ry9wlbfd6JkA4HXeYtNeHfUAzXbVfpGxdgK9IiQLdSrDnHlnL3AulpN4NvYD3h9tuFkzqkMq3loMMOloIukKcHi400jeldnXalIBIhNp/s1600/preview.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEigZhVEY5QJWaxHiPDmCZOYVOBTAVtkKuOn2eI5ry9wlbfd6JkA4HXeYtNeHfUAzXbVfpGxdgK9IiQLdSrDnHlnL3AulpN4NvYD3h9tuFkzqkMq3loMMOloIukKcHi400jeldnXalIBIhNp/s1600/preview.png" height="255" width="320" /></a></div>
<br />
ちとLabelが大きすぎるので、Labelを選択して、右側の<b>Property</b>から<b>Size Policy</b>を探しだして、<b>Vertical Policy</b>を<b>Fixed</b>にします。<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj8QpB61Ba3DyU_FD7Y8d8YXwx82q2fGuyqZK6QXRwNM2GUBTfLlgCAfR0PuYM7FUWSyZb56jjZ3IhhGJOwEwWK5fK8_MxFRnZuUanF3opP16sXTENgOBNa4h5p8LN3S_1W6mtFqUaSzMmF/s1600/property.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj8QpB61Ba3DyU_FD7Y8d8YXwx82q2fGuyqZK6QXRwNM2GUBTfLlgCAfR0PuYM7FUWSyZb56jjZ3IhhGJOwEwWK5fK8_MxFRnZuUanF3opP16sXTENgOBNa4h5p8LN3S_1W6mtFqUaSzMmF/s1600/property.png" height="320" width="188" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh0Um0jgOVDFwXMg5PzEq8bXmAmdMnAylVIzVK_bQwHXoZ80tLOkbED77sAA9a-R7nYFfaAueyUe-cWylEYOXPjQk79P8oC41VDKSL3RkGf4zsyKv0ncDF54S_F9YFjBXITNWndGLb4QMbX/s1600/qtdesigner8.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh0Um0jgOVDFwXMg5PzEq8bXmAmdMnAylVIzVK_bQwHXoZ80tLOkbED77sAA9a-R7nYFfaAueyUe-cWylEYOXPjQk79P8oC41VDKSL3RkGf4zsyKv0ncDF54S_F9YFjBXITNWndGLb4QMbX/s1600/qtdesigner8.png" height="170" width="320" /></a></div>
<br />
うん、イイ感じになってきました。<br />
LabelがTextLabelと言う名称で左寄せなんで、これをMy Moneyと改名して中央寄せにしたい。その場合は、さっきのように、Labelを選んで、右側の<b>Propety</b>の<b>Text</b>でTextLabel -> My Moneyと変更、<b>Alignment</b>の<b>Horizontal</b>を<b>AlignCenter</b>にします。<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEifW8moG6fyZpEvml0CvcKUfQQvWxA5j23mkuBf_b6cUNVAzscz5nlPdaX_GLwzyovXJi1_1pyKvmQeggriPlbTqKjKXXKUlZst97WYVqjjBd8zX3DR4oRP0_BZW0VGmhwgl-osf5TZbpRl/s1600/qtdesigner9.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEifW8moG6fyZpEvml0CvcKUfQQvWxA5j23mkuBf_b6cUNVAzscz5nlPdaX_GLwzyovXJi1_1pyKvmQeggriPlbTqKjKXXKUlZst97WYVqjjBd8zX3DR4oRP0_BZW0VGmhwgl-osf5TZbpRl/s1600/qtdesigner9.png" height="171" width="320" /></a></div>
<br />
DialogのWindowTitleをMain Viewに変更したいので、右上の<b>Object Inspector</b>から<b>dialog</b>を選択、またもや<b>Property</b>から<b>WindowTitle</b>を探しだしてMain Viewに変更します。<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgM9f9L4B1MmKivCt-R3r_DK8UK0jcDizuz33-L9jvr9-5x1JzLIiWFoeGY2Alu-3sHrhmn10aKEil3JRd4FCzZjaCN0NSWQFVEeRtuOtEySIIwsfvcq7iNBr3QOEwD9zliI_FDq1YeV2EH/s1600/qtdesigner10.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgM9f9L4B1MmKivCt-R3r_DK8UK0jcDizuz33-L9jvr9-5x1JzLIiWFoeGY2Alu-3sHrhmn10aKEil3JRd4FCzZjaCN0NSWQFVEeRtuOtEySIIwsfvcq7iNBr3QOEwD9zliI_FDq1YeV2EH/s1600/qtdesigner10.png" height="169" width="320" /></a></div>
<br />
ボタンの名前も変えましょう。これも例によって<b>Propety</b>を使います。<b>Text</b>をそれぞれAdd Money、Remove Moneyに変更します。<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiPNiec_JXoCWDxhQ5Rmkv3huMavLjckV3mvdmDHiUL0wMPUgmzhY3Xhm2EnyScPOCEnNbhLrbJo-sQyqhlAEdYQEC4ujnmgGYtuSobmiEix5mQX9hXItiKZ5wbV2xa3OzRAww8Eds7NuY2/s1600/qtdesigner11.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiPNiec_JXoCWDxhQ5Rmkv3huMavLjckV3mvdmDHiUL0wMPUgmzhY3Xhm2EnyScPOCEnNbhLrbJo-sQyqhlAEdYQEC4ujnmgGYtuSobmiEix5mQX9hXItiKZ5wbV2xa3OzRAww8Eds7NuY2/s1600/qtdesigner11.png" height="170" width="320" /></a></div>
<br />
ここまで来ればあと一歩、です。Qtには<a href="http://qt-project.org/wiki/Signals_and_Slots_in_PySide_Japanese">シグナルとスロット</a>って機構がありまして、要するにボタンが押された時適切なメッセージを出して任意のメソッドに繋ぐ(スロット)わけですが、そのひな形もQt Designer上で作ります。<br />
[Edit]プルダウンメニューから[Edit Signals/Slots]を選択してQt Designerのモードをシグナル/スロット編集モードにします。<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg_iKcnbN2cK4Wh_-Vm_mFOOSqYFrqRNBoU1-G1IGKFlLJyOgReP86mJ8mnmIOpqNepyQRyuSJYgEQwu-1kC0Q4ALEDCx4uPQPHTcjMqKpdG1JweK9kz8URBbA2795X_-Jez0b6-dqS6lFE/s1600/qtdesigner12.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg_iKcnbN2cK4Wh_-Vm_mFOOSqYFrqRNBoU1-G1IGKFlLJyOgReP86mJ8mnmIOpqNepyQRyuSJYgEQwu-1kC0Q4ALEDCx4uPQPHTcjMqKpdG1JweK9kz8URBbA2795X_-Jez0b6-dqS6lFE/s1600/qtdesigner12.png" height="168" width="320" /></a></div>
<br />
さて、そうすると任意のボタンをとあるスロットに繋ぐわけですが、このシグナル/スロットモードでは、GUIでそれを抽象的に扱う事が出来ます。例えばAdd Moneyボタンをドラッグしようとするとヘンな矢印が出てくるんですね。これが「特定のメソッドへの接続」を表します。<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgLD1aUO7Tmcm-2vV_i49DLU1xF71ETHPieXKo9hbsD5Boak0LSM9vVywNaLAmszd3MINL-coGXIch4VW0yw49eV1ZGNeL9JLIbwn5BSY12seXWsFaXGzz8ZViuB-XDjIFEyTjgog9SYPK-/s1600/qtdesigner13.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgLD1aUO7Tmcm-2vV_i49DLU1xF71ETHPieXKo9hbsD5Boak0LSM9vVywNaLAmszd3MINL-coGXIch4VW0yw49eV1ZGNeL9JLIbwn5BSY12seXWsFaXGzz8ZViuB-XDjIFEyTjgog9SYPK-/s1600/qtdesigner13.png" height="168" width="320" /></a></div>
<br />
マウスを離すとConfigure Connectionと言うポップアップが現れます。<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg5qWXMmprAkbI1qmhIJ8baHMWaAsS9Rczevjz67A8l-0o-dgy50PAy3nx7J0YH9Px_lCCoKrhi34HjCs8ttzozhahIjPHyOsndSv3SYidUqvjR3vFYAWw96rqyvU6t3RVLeBg1zXkNQLpi/s1600/configure+connection1.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg5qWXMmprAkbI1qmhIJ8baHMWaAsS9Rczevjz67A8l-0o-dgy50PAy3nx7J0YH9Px_lCCoKrhi34HjCs8ttzozhahIjPHyOsndSv3SYidUqvjR3vFYAWw96rqyvU6t3RVLeBg1zXkNQLpi/s1600/configure+connection1.png" height="211" width="320" /></a></div>
<br />
ちょっと見れば分かるんですが、左側が「ボタンの動作」、右側が「ボタンの動作に従った望まれるメソッド」が並んでて、例えば左にあるclicked()ってメソッドは「ボタンがクリックされたら」ですね。つまり、「何かが成されたら(右側にある)何かをしろ」と言う関係になっています。<br />
右側にはQtで想定されてるデフォルトの動作が並んでるんですが、ここではbuttonClicked()と言うメソッドを(まだ書いてないけど)新たに追加して、そいつと左側のclicked()を結びましょう。<br />
右側の[Edit...]ボタンをクリックします。<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEispYsA7Xc5xSIriV3z25yprgbXcDInQY8IV6kWwLq2gMrJqgopYIlmcnz672fktpiAejbp_RGe2bBocnl_gzHMzL8DpHi9pakS0fwBaZ-XvcxfiKdcAd2Nqdo4KBNqn0547JaEX3e5CpzB/s1600/signal+slot+dialog.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEispYsA7Xc5xSIriV3z25yprgbXcDInQY8IV6kWwLq2gMrJqgopYIlmcnz672fktpiAejbp_RGe2bBocnl_gzHMzL8DpHi9pakS0fwBaZ-XvcxfiKdcAd2Nqdo4KBNqn0547JaEX3e5CpzB/s1600/signal+slot+dialog.png" height="289" width="320" /></a></div>
<br />
上がスロット、下がシグナルになっています。今回はシグナルは使わないんで、上のスロットの+ボタンを押してbuttonClicked()と言うメソッドを追加します。<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgkRSZY-ISOb9A-bjiWUzdCp7oY71dYlSl897dj6BeBq7yKMmwOleqoIaj-c1QGSHiuowf36WMOx3tFkIzuS55ZVM9FGTVjOgJ9nV9p5iet_uey_yY86bgQOho-wuBn7R5MVwEF9VhzPi7W/s1600/signal+slot+dialog+2.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgkRSZY-ISOb9A-bjiWUzdCp7oY71dYlSl897dj6BeBq7yKMmwOleqoIaj-c1QGSHiuowf36WMOx3tFkIzuS55ZVM9FGTVjOgJ9nV9p5iet_uey_yY86bgQOho-wuBn7R5MVwEF9VhzPi7W/s1600/signal+slot+dialog+2.png" height="289" width="320" /></a></div>
<br />
[OK]ボタンを押してConfigure Connectionに戻ります。左側からClick()を選択、右からbuttonClicked()を選択、[OK]ボタンを押します。<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiQVQm_6PYxyxynZqfTbQPvh9FQOVO4N9O1nYLF6CslJj6zzzhyphenhyphen4gi7fmMKXJv0E3dGpDE5qiWC7YFW9WweMj46gb0dh92me0E-OgrqXIuDzZHvk1wXkmuT_oAz9iRU-6uL5LjhHdjTl4q1/s1600/configure+connection+2.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiQVQm_6PYxyxynZqfTbQPvh9FQOVO4N9O1nYLF6CslJj6zzzhyphenhyphen4gi7fmMKXJv0E3dGpDE5qiWC7YFW9WweMj46gb0dh92me0E-OgrqXIuDzZHvk1wXkmuT_oAz9iRU-6uL5LjhHdjTl4q1/s1600/configure+connection+2.png" height="211" width="320" /></a></div>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj6OWDtFfaQ5xPU4_okvh17uhclDEx9fX7PqeiZ-TiFYnQ92hbKovfftJgg8ikYjJ43h0yb6IjB-DS6deLYCPK0TKfSYzSXoBecuqSd27ePCT6Q9nf_sNHffJ3dMPKQucKPJVbleqvEzc8s/s1600/qtdesigner14.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj6OWDtFfaQ5xPU4_okvh17uhclDEx9fX7PqeiZ-TiFYnQ92hbKovfftJgg8ikYjJ43h0yb6IjB-DS6deLYCPK0TKfSYzSXoBecuqSd27ePCT6Q9nf_sNHffJ3dMPKQucKPJVbleqvEzc8s/s1600/qtdesigner14.png" height="165" width="320" /></a></div>
<br />
同様にして、Remove Moneyボタンも同じbuttonClicked()メソッドに繋いでしまいます。<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjtc-1hblHzHNTcibowpe_2-Wr6ZnFA1i8NObO8xhtR9hWKvETDMVoS7ql8CK72Uf5rBg0s82S1b_h1FblFSQHP6YycyNcPjiHecE58FT8DDEUFTeod1YSGRGEcxBua9fTPhvW1vY7uS-sj/s1600/qtdesigner15.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjtc-1hblHzHNTcibowpe_2-Wr6ZnFA1i8NObO8xhtR9hWKvETDMVoS7ql8CK72Uf5rBg0s82S1b_h1FblFSQHP6YycyNcPjiHecE58FT8DDEUFTeod1YSGRGEcxBua9fTPhvW1vY7uS-sj/s1600/qtdesigner15.png" height="168" width="320" /></a></div>
<br />
これで準備はO.K.です。まずはui_mvctest.uiとでも名づけて、GUIデザインを保存しましょう。<br />
Qt Designerではxmlファイルとしてデザインが保存されます。<br />
<br />
<script src="https://gist.github.com/anonymous/8640154.js"></script><br />
<br />
んで、まずはこいつをPythonで解釈出来るpyファイルに変換せなアカンのですね。んで、その為のツールが、例えばWindowsならC:\Python27\Scripts\にpyside-uic.exeと言う名前でインストールされてる筈なんですが、どーゆーわけかコイツはコマンドラインのアプリケーションとなっています(苦笑)。何故にQt Designerから直接呼び出して変換出来ないのかサッパリ分かりません(苦笑)。<br />
(<a href="http://wxglade.sourceforge.net/">wxGlade</a>ならGUIでPythonコードに変換してくれます!)<br />
Linuxならこの手のツールがあっても、コマンドラインが使いやすいんでイイんですが、ことさらWindowsのコマンドラインは使いづらいんですよねぇ。パス記述するのがおうおうにして厄介です。<br />
まあ、しょうがないんですが、基本的には完全パス与えて実行した方が良さそうです。例えばこの場合ですと、<br />
<br />
<blockquote class="tr_bq">
C:\Python27\Scripts\pyside-uic.exe -o プロジェクトフォルダ\ui_mvctest.py プロジェクトフォルダ\ui_mvctest.ui</blockquote>
<br />
でしょうか。オプション引数 -oを忘れないようにしましょう。また、こう言うのがホント気になるんですが、変換後のファイル、元ファイル、の順序です(普通の発想なら元ファイル -> 変換後のファイル、になるんじゃねえの?とか思うんですが、しばしばこう言う「語順」を見かける)。<br />
さて、無事に変換が済めば、PySide用に変換されたpyファイルが出来てる筈です。<br />
<br />
<script src="https://gist.github.com/anonymous/8640336.js"></script><br />
<br />
ってなわけでこいつを利用してMVCモデルによるGUIアプリケーションを作っていきます。<br />
<br />
<h3 style="text-align: left;">
残念なお知らせ</h3>
当初の予定では、CLIのアプリをREPLモデルとして作る -> 各クラスとGUI部品を多重継承で受け取ったMVC部品を作ってく、って予定だったんですが、なんと、PySideが多重継承させてくれないんですよ(苦笑)。何でやねん、とか怒ってたんですが(笑)。<br />
曰く、<br />
<br />
<blockquote class="tr_bq">
新スタイルクラスじゃないと多重継承出来ません</blockquote>
<br />
とか言いやがって(笑)。んなもん知るか、っての(笑)。<br />
調べてみたら、Pythonの新スタイルクラス、ってのは2.2以降に出てきた、とか書かれてるんですが、Pythonに触れ出したのは2.4以降なので、全く何のこっちゃ、です(笑)。<br />
まあ、いずれにせよ、Qt自体が多重継承推奨してない模様なんで従いますか。この場合の「従う」ってのは、結果REPLのPrintは捨てる、って事です(爆)。<br />
<br />
<h3 style="text-align: left;">
View</h3>
と言うわけで、まずはViewを作っていきます。Viewのコードは以下の通り。<br />
<br />
<script src="https://gist.github.com/anonymous/8640773.js"></script><br />
<br />
ポイントは、最初書いた通り<b>Controllerのインスタンスを保持してる</b>事、それと、REPLモデルの場合の引数の一部にあたる<b>環境 env をインスタンス変数として持っている</b>事です。<br />
単純な流れとしては、<br />
<br />
<br />
<ol style="text-align: left;">
<li>ボタンがクリックされると buttonClicked() メソッドが呼び出され、その中でControllerのインスタンスのparse()メソッドが呼び出され(結果Model()のインスタンスが返す)返り値をxとenvに代入する。</li>
<li>返り値xはLCDディスプレイの表示に使われる。</li>
<li>返り値envはインスタンス変数envに代入される。</li>
</ol>
<div>
です。</div>
<div>
今回はシンプルだったんですが、基本的にはボタン押されたと同時にController(翻ってはModel)からの返り値利用して表示までこぎつけるようにした方が良いでしょうね。ここで色々分けてしまうと(つまり、別の何かに手渡す感じにする)、GUIのコード的には相当ワヤクチャになってしまうでしょう。</div>
<div>
<br /></div>
<h3 style="text-align: left;">
Controller</h3>
<div>
Controllerのコードは以下の通りです。</div>
<div>
<br /></div>
<div>
<script src="https://gist.github.com/anonymous/8641078.js"></script><br /></div>
<div>
<br /></div>
<div>
<br /></div>
<div>
ControllerもModelのインスタンスを持っています。ここではparseメソッドをオーバーライドしてますが、特徴としては、ReadはreturnしてたトコをModelのインスタンスからinterpメソッドを呼び出して引数を与えてるトコですね。まあ、見た通りReadのコードより(継承も相まって)相当単純化されています。<br />
まあ、今回はシンプルなんですが、こう言う感じで、例えばViewが複数の情報を送るような場合、View側から適切なControllerインスタンスのメソッドを呼び出し、Controller側でも入力に応じたメソッドを用意しておく、と言うようなカタチにすれば複雑なプログラムにも対処出来るんじゃないでしょうか。かつ、ここではやっぱ「計算自体は」行いません。あくまでModelに対するプロトコル形式を選択出来るカタチに注力すべきだと思います。<br />
<br />
<h3 style="text-align: left;">
Model</h3>
最後はModelです。Modelのコードは以下の通りです。<br />
<br />
<script src="https://gist.github.com/anonymous/8641136.js"></script><br />
<br />
<br />
これも継承の為相当シンプルになっています。まあ、このアプリケーションのケースだと、元々Eval自体もシンプルなんですが、GUIの場合余計な入力は最初っから入ってこない前提なんで(特にボタンとかはそう)殆ど書くのはハナクソですね。<br />
もう一回書いておきますが、<br />
<br />
<blockquote class="tr_bq">
View が Controller のインスタンスからメソッドを呼び出し -> Controller は Model インスタンスからメソッドを呼び出し -> Model インスタンスが計算結果を返す -> View が結果を知る</blockquote>
って流れなんですが、恐らくこのように見えるでしょう。<br />
<br />
<blockquote class="tr_bq">
View が Controller のインスタンスからメソッドを呼び出す -> すぐ返り値が返ってくる</blockquote>
<br />
つまり、Model自体は表面的には見えないで隠されてるカンジになりますね。<br />
<br />
<h3 style="text-align: left;">
Main</h3>
<a href="http://zetcode.com/gui/pysidetutorial/">ZetCodeのPySideチュートリアル</a>に依ると、main関数を作って、こう書くのが「お約束」の模様です(笑)。<br />
<br />
<script src="https://gist.github.com/anonymous/8641263.js"></script><br />
<br />
<br />
<br />
ってなわけで REPL -> MVC のコンバートは完了です。以下にMVCの全コードを置いておきます。<br />
<br />
<script src="https://gist.github.com/anonymous/8641287.js"></script><br /></div>
<br /></div>
<h3 style="text-align: left;">
ついでだからWindowsの実行形式も作ってしまおう</h3>
せっかくGUIのアプリケーションが作れたんだからWindowsの実行形式にコンパイル出来ればサイコーだったりしますよね。その為には<a href="http://www.py2exe.org/">py2exe</a>と言うユーティリティを使います。<br />
これは以前、Pythonでの<a href="http://beta-reduction.blogspot.jp/2013/12/blog-post.html">イスカンダルのトーフ屋ゲーム</a>の実行形式作った時にも利用したユーティリティでコマンドライン形式のアプリケーション実行形式を作る場合には結構簡単に使えるんですが、ところがQtなんかのGUIモノをコンパイルする場合ハマったので、一応ここに解説しておきます。<br />
まずは何はともあれ、どーゆーわけか<br />
<h1 bi:title="item" style="background-color: white; color: #4f4f4f; display: table-cell; font-family: 'Segoe UI Light', 'Segoe UI', Arial, Verdana, Tahoma, sans-serif, 'Microsoft JhengHei', 'Microsoft YaHei', Meiryo, 'Malgun Gothic'; font-size: 30px; font-weight: normal; line-height: 1.13em; margin: 0px 10px 0px 0px; padding: 0px 0px 0px 15px; vertical-align: middle;">
<br /><a href="http://www.microsoft.com/en-us/download/details.aspx?id=29">Microsoft Visual C++ 2008 Redistributable Package (x86)</a></h1>
<br />
と言うものがPCにインストールされてないといけません。しかもSP1とか2010とかじゃダメで、上のブツじゃないとダメなんです。こいつに含まれてるmsvcp90.dllってブツが実行形式作成に必須なモノなんです。<br />
なお、上のヤツは再配布可能なdll(ダイナミックリンクライブラリ)が含まれてるんですが、利用規約にはフォルダMicrosoft.VC90.CRT(msvcp90.dllが存在するVC以下のフォルダ)に含まれてる「3つのdll」は全て単独で配布するのは禁止されていて、同じフォルダにあるMANIFEST ファイルを含め、全部纏めて配布せなアカン、って決められてる模様です。<br />
ああ、クソメンドくせえなぁ、Microsoftさんよ(苦笑)。<br />
そこで、<a href="http://www.py2exe.org/index.cgi/Tutorial#Step52">py2exeチュートリアル</a>に従って、まずは次のようなsetup.pyを作ります。<br />
<br />
<script src="https://gist.github.com/anonymous/8642423.js"></script><br />
<br />
上のようなsetup.pyを作ると、自動生成される実行形式をぶち込んだフォルダ(distフォルダ)にdllが入ってるフォルダをコピペしてくれます。これでMicrosoftの規約は守れるようになるわけですね。<br />
そしてその後、<br />
<br />
<br />
<ol style="text-align: left;">
<li>DOS窓でプロジェクトフォルダに移動する</li>
<li>「python プロジェクトフォルダ/setup.py py2exe」 をDOS窓で走らせる </li>
</ol>
<div>
とすればプロジェクトフォルダ内に新しくbuildとdistと二つのフォルダが作られます。distフォルダにめでたくWindows実行形式が生まれていますね。</div>
<div>
<br /></div>
<div>
まあ、こんなカタチでやっとこさ、GUIアプリの実行形式を作るまで辿り着きました。お疲れ様です。</div>
<div>
<br /></div>
<div>
<iframe frameborder="0" height="120" scrolling="no" src="https://skydrive.live.com/embed?cid=C5297123E0B737C0&resid=C5297123E0B737C0%21183&authkey=AIngUJ-4csfKK_U" width="98"></iframe><br /></div>
</div>
cametan_001http://www.blogger.com/profile/04882440058326617382noreply@blogger.com0tag:blogger.com,1999:blog-4018043311243103708.post-8547482899953513532014-01-05T10:26:00.002+09:002014-01-05T10:42:09.515+09:00Return Of イスカンダルのトーフ屋ゲーム<div dir="ltr" style="text-align: left;" trbidi="on">
シリーズ(いつシリーズ化したんだ・笑)「イスカンダルのトーフ屋ゲーム」第3弾です。<br />
<br />
まあ、今回は余談的に始めました。OOPスタイルでのSchemeでの「イスカンダルのトーフ屋ゲーム」の作成にかなり苦労したんで、フツーに関数型で書けばどうなるのか、自分で確かめてみたかったんですね。<br />
<br />
作成自体は1日あれば終わったんですが、やっぱり慣れもあるし、ロジック的には同じの3回目だから、って事もあるんでしょうが、それでも「関数型で書く」ってのはやっぱ早いです。すぐ出来る。<br />
<br />
加えるとテストがラクなんですよね。オブジェクトに対してメソッド呼び出して・・・ってのは、ぶっちゃけ、インタプリタでも書式がメンド臭いんで、結局「テストコード」を本体側に対して記述しないと色々とウザったい。<br />
ある意味、テスト駆動開発ってOOP前提だよな、って気がしました。<br />
<br />
なお、今回は原作者のページに置いてある<a href="http://www.alles.or.jp/~sir/vivi/dat/tofu_pro.lzh">イスカンダルのトーフ屋ゲーム(ViViScript 版)~プロ仕様~</a>の計算ロジックを参考にしてます。これ(と同じページに置いてあるオリジナル版)が原作者氏の書いたソースだ、って気づかなかったんですよね(笑)。実行形式が端末起動して遊べるようになってたから、まさか今公開してるのが<a href="http://vivi.dyndns.org/">エディタ</a>のマクロ言語で記述されてる、たぁ思いもよらなかったです(笑)。てっきりCかなんかで記述してると思い込んでました(爆)。<br />
<br />
ってなわけで、初めましょうか。<br />
<br />
<b>ゲームに必要な大域変数の定義</b><br />
<br />
まずはゲームに必要な大域変数を定義します。<br />
関数型プログラミングではあまり大域変数使わない前提なのは確かなんですが、計算の過程に於いて、何らかのデータの「参照対象」が必要な場合があります。<br />
重要なのは「参照の為に」定義するんであって、そのデータは絶対プログラム内では書き換えません。<br />
まあ、そいつを引数で与えてやってもイイんですが、後に見ますが、スタイル的にはフツーのプログラミングに於ける「破壊的変更」を対象を新しく生成する事で避けてるんで、どうしても本体の引数が増えちゃうんですね。<br />
ここで「大域変数」として定義した内容は、ゲーム全体に於いて使いまわされるけど、固定した値で変更する必要が生じないモノに限ってます。具体的には、<br />
<br />
<br />
<ol style="text-align: left;">
<li>トーフのプロパティ(値段、製造コスト等)</li>
<li>Game-Overに達する目標金額</li>
</ol>
<div>
の二つです。今までの「イスカンダルのトーフ屋ゲーム」に於けるプログラミングを見てた人には、この二つはゲームを通して不変である、って事に気づくでしょう。<br />
(ちなみに、ゲームに於いて表示される「文字列」も不変な故に大域変数対象になりますが、それは後にPrint部を作る際に定義しましょう。結局トータルでは3つの大域変数が必要となります。)</div>
<div>
また、プログラム本体にこの手の「決まった数値」を埋め込むのは避けるべきだ、ってのも良く言われる事です。これらを関数の外側に出しておけば、ソースに手を入れて変更するのもラクですしね。</div>
<div>
これら二つをトーフに於いては連想リスト、目標金額は単なる変数として定義します。</div>
<div>
<br /></div>
<div>
<br /></div>
<br />
<script src="https://gist.github.com/anonymous/8261977.js"></script>
これで準備は万端です。ではまずはR->E->Pの順序に則って、まずはRead部から仕上げて行きましょうか。<br />
<br />
<b>Read (Parser 関数)</b><br />
<br />
大まかなロジックは前回と全く同じなんですが、クラスに詰め込んだローカル関数を基本的には全部外に出してフツーの関数として再定義する事で、ソースは随分スッキリと読みやすくなります。<br />
Common Lispに比べると、まあ、名前空間の衝突とか避ける為に、割にSchemeでは何でもかんでもローカル関数にしたがる傾向がありますが(実際、外人のCLerに「キミのコードはScheme臭い」って言われた事がある)、確かにローカル関数は場合によってはソースの可読性落としますね。<br />
(ちなみに、基本的にはCLerはSchemer程ローカル関数は使わずに何でも大域的に定義したがるらしい。と言うのも「<a href="http://www.ai.mit.edu/projects/iiip/doc/CommonLISP/HyperSpec/Body/mac_defpackage.html">パッケージ機構</a>」ってのがあって、名前空間の問題を仕様上回避するような仕掛けが施されている。反面そう言う大掛かりなシステムが"仕様上定義されてない"Schemeでは比較的神経質に関数を扱わざるを得ない。)<br />
まあ、ケースバイケースなんですが、前回みたいに「ローカル関数の中にローカル関数定義」とかは多分やり過ぎなんで(笑)、そこまでするなら大域的に定義した方がスッキリするのは確かです。<br />
<br />
なお、大まかな仕様は次の通りです。<br />
<br />
<br />
<ol style="text-align: left;">
<li>Parser関数(以降Readとする)は6つの(!)引数を受け取り、その中のphase変数の値を見て挙動を変更する。</li>
<li>変更される挙動とは...もういいやね、これ(笑)。</li>
<li>基本的には、引数をそのまま6つの多値として返すが、入力値があった場合にはそれをpnに束縛する。</li>
</ol>
<br />
<br />
ではソースです。<br />
<br />
<script src="https://gist.github.com/anonymous/8262162.js"></script>
<br />
だいぶスッキリとして読みやすくなったのではないでしょうか。ローカル関数(<a href="http://www.schemers.org/Documents/Standards/R5RS/HTML/r5rs-Z-H-7.html#%_idx_132">letrec</a>)も相変わらず使ってますが、やっぱ一つの関数内にネストが一個程度でやめておきたいもんですね。ローカル関数内ローカル関数は見た目には厳しすぎる。<br />
<br />
ところで、実はここで前回までのプログラミングと大きく違う点があります。それは3つあって、<br />
<br />
<br />
<ol style="text-align: left;">
<li>環境を作る仕組みがない。</li>
<li>環境は関数に引き渡される引数として表現される。</li>
<li>従って、ReadにもPrintにも(言わせれば)環境が引数として渡される。</li>
</ol>
<div>
前に<a href="http://beta-reduction.blogspot.jp/2013/12/blog-post.html">Python版から作り出した時</a>、「環境はEvalのモンだしなぁ」ってこだわってたんですが、実はここに来てそんなこだわりは必要がない、って事に気づきました。以前はそこにこだわっていた為、クラス変数と言うポスト大域変数の書き換え、みたいな破壊的操作が必要になってたんですね。</div>
<div>
しかしながら、ここに来て、環境を手渡しつつRead、Eval、Printの間でグルグル回す方が良いのではないか、と気づいた。つまり、どの環境の変数に対してどの関数が手を入れるのか明確にする方が大事な気がしてきたんです(言わばバケツリレー方式ですね・笑)。</div>
<div>
基本的には環境に対して計算結果を返すのはEvalの役目です。それは変わりません。しかしRead部はどっちにしても入力値を手渡す、って限定に於いては環境に手を入れても良いのではないか。つまり、ここでは「入力情報」がある意味「環境」になっちゃった、って事ですね。</div>
<div>
上にあげた6つの引数のうち、 pn ってのが入力値を保持する為の引数です。つまり、何らかの入力があった際には pn は Readが扱う、と言うルールが明確になったのです。</div>
<div>
<br /></div>
<div>
<b>Eval (Interp関数)</b></div>
<div>
<br /></div>
<div>
前までだとやれこのデータをクラスで定義して・・・だったんですが、今回は全く必要がありません(笑)。サクサクと進みます(笑)。</div>
<div>
Eval部も基本的には、前回の「ローカル関数だらけ」を分離したスタイルになっています。お陰で、Eval(Interp関数)が一体何を行ってるのか、明確になる。</div>
<div>
<br /></div>
<div style="text-align: left;">
<ol style="text-align: left;">
<li>Interp関数(Eval)は6つの引数を受け取る。</li>
<li>シンボルで表された変数phaseを見て適する関数を呼び出し、次のphaseを束縛する。</li>
<li>変数 player はプレイヤーの持ち金である。加算減算が( calc 関数によって)返された場合は変数 player にその返り値を束縛する。</li>
<li>変数 comp は対戦相手の持ち金である。加算減算が( calc 関数によって)返された場合は変数 comp にその返り値を束縛する。</li>
<li>変数 wr は天気予報の値である。関数 weather-report の返り値を束縛する。</li>
<li>変数 pn はReadが読み込んだ入力値である。計算に必要な場合、この値を見て評価するが、pn に対しては特に何も行わない。</li>
<li>変数 cn は評価に於いて何らかのエクストラの情報が生じた場合(これは要するにPrintが要する情報になるが)この変数に評価結果を束縛する。</li>
<li>以上の6つを多値による返り値として返す。</li>
</ol>
<div>
さて、こう見てみると、一応Read->Eval->Printループなんですが、内部的にはEvalがスタート地点で、Eval->Print->Readと言うカンジで回っていくのが分かるんじゃないでしょうか。あらゆるデータに関する「評価」は文字通りEval(interp関数)が行っているんです。</div>
<div>
<br /></div>
<div>
ではソースの方を。</div>
<div>
<br /></div>
<div>
<script src="https://gist.github.com/anonymous/8262705.js"></script><br /></div>
<div>
<br /></div>
<div>
やっぱ分かりやすいと思います。Evalが必要とする個々の関数も全部小さいですし、ローカル関数で纏められすぎてるコードを読むよりゃやっぱラクですよねぇ。<br />
一つあるトリックとしては、関数 actual-weather が多値を返してます。一つ目は一種フラグのシンボルで、このシンボルを使ってPrint関数が文字列情報をまとめた連想リストから適する表示形式を探してきます。つまり、変数 cn に束縛させるシンボルを返すと言うのが目的。二つ目はトーフデータが持つ「天気による最大売上個数」を連想リストから検索してて返してて、この結果はプレイヤーと対戦相手の「売上計算」に利用されてその後破棄されます。<br />
このくらいですし、大したトリックでもないですね(笑)。まあ、多値利用しただけだった、ってばだけなんですが、意外と便利です(笑)。<br />
<br />
<b>Print 関数</b><br />
<br />
さて、最後に Print 関数です。今回ははえぇなぁ(笑)。<br />
最初に書きましたように、まずは表示に使うメッセージデータを連想リストとして纏めて大域変数としてまずは定義します。<br />
実はこの辺、<a href="http://beta-reduction.blogspot.jp/search?updated-min=2014-01-01T00:00:00%2B09:00&updated-max=2015-01-01T00:00:00%2B09:00&max-results=1">前回も指摘した</a>んですが、<a href="http://www.chino-js.com/ja/tech/srfi/srfi-48/">SRFI-48で定義されてるFormat</a>を使用すれば、書式の指定子込みでデータを作れるんで、もっと簡単になるでしょう。<br />
今回もPrintが利用してる関数は、わざわざ整形演算を行う為に書いたモノが殆どで、format 前提なら殆ど本体だけで間に合う、って言っても過言じゃないと思います。<br />
<br />
<br />
<ol style="text-align: left;">
<li>Print 関数は6つの引数を取る関数である。</li>
<li>ただし、印字を出力後、この6つの引数を多値としてそのまま返し、引数に対する操作は一切行わない(これがまさしく印字が副作用的性質である事の証明・笑)</li>
<li>基本的には、phase 引数を見て適した文字列を連想リストとして設計された message データから検索してくる。</li>
<li>出力に必要であれば、cn 変数を用いる。</li>
</ol>
<div>
これだけ、です。コードがそれにすれば分量が若干多いように見えますが、もう一度言いますが、SRFI-48でのformatとか利用すれば殆ど Print 関数本体だけで仕事をやっつけちゃえるのです。</div>
<div>
(恐らく連想リストの設計さえも、phase と一対一対応で、理想的には phase を利用して検索して表示するだけ、までに構造的には絞り込めると思います。Lispのシンボルの有用性の証明になる気がします。)</div>
<div>
<br /></div>
<div>
ではソースです。</div>
<div>
<br /></div>
<div>
<script src="https://gist.github.com/anonymous/8262870.js"></script><br /></div>
<div>
<br /></div>
<div>
とこれで、Read、Eval、Printの全てのパーツが「関数型プログラミングを使って」組みあがりました。(もっともHaskellな人達は簡単に出力を扱える事に違和感があるでしょうが・笑)<br />
あとはREPLを組むだけ、ですね。<br />
<br />
<b>Read-Eval-Print-Loop</b><br />
<br />
REPLは次のように末尾再帰な関数として書いて、実行時に適切な引数をREPL関数に渡す事でゲームが動きます。<br />
<br />
<script src="https://gist.github.com/anonymous/8262930.js"></script><br />
まあ、多値を6つも返す関数が3つもあるんで物凄い事になってるんですが(笑)、基本的には簡単ですね。関数REPLは末尾再帰で呼び出された際に、最終的にPrintが渡した値を引数として持って、延々と無限ループへと突入するわけです(笑)。Evalが(exit)命令を出すまでこの動きは止まりません(笑)。<br />
<br />
なお、phase の初期値として #f を渡してるのは、初期状態で、 #f はParser 関数( Read ) 本体のcase 構文内で「else」として認識され、そのまま「何もせずに」 interp 関数に全引数を渡すからです。<br />
続いて、interp 関数は phase = #f を見て、やはり本体の case 構文内で else として処理され、ここで初めて、phase にシンボル 'introduction が返り値の一つとして束縛されるわけです。<br />
つまり、初期状態で phase に与える値はゲーム中で使われてないブツなら何でも良くって(あるいは意図的に「途中から」始める事も可能) 結果、伝統的で単純な #f を与えてみました。<br />
ちなみにそうなると、最初の Read -> Print 間は全く何もしてないに等しくて、引数与えるのは「REPLをビックリさせて起動させる」に等しいです(笑)。また、先ほどにも書きましたが、内部的にはE->P->Rの順に処理が進んでいく、ってのは全くそのままの意味です。<br />
<br />
<b>非破壊的なゲーム構造</b><br />
<br />
まあ、ちょっとしたシャレで始めたこのヴァージョンだったんですが、面白い事に気づきました。<br />
実は、REPLで末尾再帰が呼び出される際に、与えられる引数はPrintの返り値です。<br />
んで、実はこのプログラムの中では「一回も」普通のプログラミングで言う「代入」は行われていないんですね。つまり「直接変数を書き換える」と言う事は一切行っていない。<br />
特にEvalの動作なんですが、<br />
<br />
<br />
<ol style="text-align: left;">
<li>引数を参照する</li>
<li>基本的には「操作」は新しい値を「作る」</li>
<li>他のケースでは「与えられた引数を参照して」(要するに)コピーしてる</li>
<li>結果、返り値は元のデータではなく新しいデータである。</li>
</ol>
<div>
この「コピーして」「新しいデータを作る」ってのが言わば関数型プログラミングのキモなんですよね。じゃあ参照に使われた古い引数はどうなるのか、っつーと、参照されなくなった時点でGC(ガベージコレクタ)がやってきて回収・廃棄処分です(笑)。</div>
<div>
これは実はReadにせよPrintにせよ、「コピーして新しいデータを作ってる」ってのは同じです。「書き換え」ではないんです。</div>
<div>
まあ、こんなこたぁ関数型言語に詳しい人には釈迦に説法なんですが、ちと待てよ、と。こんな事が出来るなら恐らく「非破壊的に」データ生成し続けてゲームを作れるモデル、例えば理論的にはRPGなんかも作れるんじゃないか、って思ったんですね。で、多分それは可能でしょう。</div>
<div>
以前、関数型プログラミングは机上の空論なんじゃないか、って実は失望してたんですが、ひょいとしたキッカケで実用上OKなんじゃないか、って思えるようになったのは大収穫でした。</div>
<div>
つまり、上のREPLのような関数を設計して、参照する環境として引数(例えばRPGで言うとプレイヤー情報とか)を与えて、内部的にはコピーして元データには一切手をかけない。そうすれば「非破壊的なプログラム構造を持った」関数型による関数型の為の関数型のゲームが作成可能だ、って光明が見えたんですよね。</div>
<div>
引き続きこのテーマを研究してみたいと思います。言い換えると、例えばOOPモデルでさえ(プログラミング言語の末尾再帰の最適化が保証されていれば)インスタンス変数を「書き換える」のではなくって「新しいインスタンスを」常に生成するモデルも可能だろう、って事ですから。</div>
<div>
<br /></div>
<div>
では今回の全ソースコードです。</div>
<div>
<br /></div>
<div>
<script src="https://gist.github.com/anonymous/8263103.js"></script><br /></div>
<div>
<br /></div>
</div>
</div>
</div>
</div>cametan_001http://www.blogger.com/profile/04882440058326617382noreply@blogger.com0tag:blogger.com,1999:blog-4018043311243103708.post-20433475276725790422014-01-05T05:44:00.000+09:002014-01-05T10:26:59.108+09:00イスカンダルのトーフ屋ゲーム Strikes Back<div dir="ltr" style="text-align: left;" trbidi="on">
年末年始かけてイスカンダルのトーフ屋ゲームの実装ばっかやってて一体何をやってるんでしょうか(笑)。<br />
<br />
さて、今回は前回と同じゲームなんですが、Schemeで実装してみました。<br />
<br />
SICPのデータ抽象型のプログラミング示唆にもある通り、クロージャを使いまくってプログラミングすればどうなるのか、一回やってみたかったんですね。それがOOPの理解も深めるだろう、とか思って・・・。<br />
<br />
結果は・・・サイテーです(笑)。<br />
まあ、言語形式もあるんですが、少なくともSchemeでOOP系のプログラミングやるのは頭痛の種以外の何者でもない、ってことが分かりました(爆)。<br />
取り合えず関数型プログラミングとOOPとどっちがマシか、って話は脇に置いておきますが、少なくともLispのようなカッコが多い、と言うかプログラミングコードがかなり深いネストを生じる言語ですと、OOPのコード書くのはキツい、かつ、読むのもキツい、って思いますね(笑)。OOPはPython等のフツーの言語、言い換えるとネストがそんなにキツくならない言語に限って「スタイル的に有効だ」ってのが今回の実験での結論です。<br />
Schemeのスタイル(あるいはLisp一般での関数型プログラミングのスタイル)、ってのは、基本的には<br />
<br />
<br />
<ol style="text-align: left;">
<li>暗黙のbegin(あるいはprogn)をアテにしない。</li>
<li>従って一つの関数には一つの処理しかさせない小さな関数をたくさん書く</li>
<li>小さな関数同士を組み合わせて大きな処理をさせる</li>
</ol>
<div>
って事で、極めてシンプルにプログラムを組み上げる事です。これはUNIX的プログラミングの方策としては王道ですし、かつ、これだと大してネストが深くならないので、カッコだらけでもそんなに読むのはツラくありません。</div>
<div>
反面、OOPと言うかデータ抽象型ですと、一つのオブジェクトに如何にしてたくさんのデータと処理をまとめるか、って事になって、一つのdefineされたモノが異様に膨らんでしまいます。もうこうなると理想的なSchemeコードとは言えないでしょう。</div>
<div>
ま、てなワケで実験としては有効でしょうが、真似しないようにしましょう(笑)。自分で書いてて「こりゃヒドいコードだ」とかアタマ痛くなってましたから(笑)。</div>
<div>
<br /></div>
<div>
さて、今回の方針です。</div>
<div>
<br /></div>
<div>
<ol style="text-align: left;">
<li>新しいロジックは採用せず、基本的に前回作ったPython版のコーディングを参考にする。</li>
<li>Schemeデファクトスタンダードの仕様、<a href="http://www.schemers.org/Documents/Standards/R5RS/HTML/">R5RS</a>になるたけ準じて、Racket実装依存の機能はなるべく使わない。使った場合は明示して、他のScheme処理系で動かす場合、最低限の変更でOKなモノを目指す。</li>
<li>足りない機能があったら<a href="http://www.chino-js.com/ja/tech/srfi/final-srfis.html">SRFI</a>から持ってくる。</li>
</ol>
<div>
の3つです。1番目は当然で、「実験」でスクラッチからロジック考えてればアタマ痛くなってきます(笑)。</div>
<div>
2番目もこれがなかなかSchemeだと難しいんですが敢えての挑戦です。どうも個人的な意見では、Webで見つけた面白そうなSchemeコードでも、作者の処理系選択によって、あっちじゃ動くけどこっちじゃ動かん、とか結構腹が立つんですよね(笑)。特に僕が使ってる<a href="http://racket-lang.org/">Racket</a>の場合、Racket実装依存で書くには便利な機能がテンコ盛りなんですが、他のScheme処理系じゃ動きません、っつーのはあまりにもアレなカンジがします。</div>
</div>
<div>
3番目も初めての挑戦ですかね。SRFIは「共通ライブラリ」とか言われたりしますが、本当は個人的にはあんま好きじゃなくって、と言うのも仕様定義だけで実装は各実装に任せる、と。要するに外部的にライブラリがあって、どのScheme処理系でもインポート出来る、ってポータブルな意味でのライブラリじゃないんで嫌なんです(笑)。まあでも、今回は、少なくとも<a href="http://practical-scheme.net/gauche/index-j.html">Gauche</a>と<a href="http://www.gnu.org/software/guile/">Guile</a>の2つで最低限の変更で動かせる為にSRFIサポートをチェックしてそのようにしました(Scheme48も考えたんですが、こいつはそもそも使えるSRFIが少ない)。</div>
<div>
<br /></div>
<div>
では始めます。</div>
<div>
<br /></div>
<div>
<b>データ抽象化: 擬似OOP</b></div>
<div>
<br /></div>
<div>
ところで。「実装依存の機能をなるたけ使わない」「なるべくR5RS内で事を済ませる」となると・・・データ抽象型プログラミングを実現するにはそれなりの機構と自分で実装せなあきまへん(苦笑)。</div>
<div>
もちろん、SICPよろしく、全部細やかにクロージャ使って実装していくのも可能なんですが、それじゃああんまりにもメンド臭すぎます。</div>
<div>
そこで。</div>
<div>
<a href="http://www.seshop.com/product/detail/12043/">Paradigms of Artificial Intelligence Programming</a>を参考にして、まずは擬似OOPとも言えるシステムをR5RSマクロで組んでいくのが最初に行う事です・・・ってこれがまた大変だったんですけどね(苦笑)。</div>
<div>
ではまずそのコードを。<br />
<br />
<script src="https://gist.github.com/anonymous/8227114.js">
</script>
</div>
一応、Schemeと言うかLisp一族に明るくない人の為に解説しておきますが、簡単に言うと、Lisp/Schemeで言う「マクロ」とは、言語に新しい構文規則を付け足す機能です。つまり、Scheme/Lispでは、極端に言うと、C言語で言うdo~whileなんかの「構文」を自分で新しく作れちゃうんですね。<br />
ここではそう言うカタチで新しく「擬似OOP」な機構を作り出してるんです。<br />
んで基本的なコードのアイディアってのは<a href="http://mitpress.mit.edu/sicp/full-text/book/book-Z-H-19.html#%_chap_3">SICP第三章</a>で紹介されてるモノとも同じですね。<br />
<br />
ところで、残念ながらSchemeの<a href="http://www.schemers.org/Documents/Standards/R5RS/HTML/r5rs-Z-H-7.html#%_sec_4.3">R5RSで策定されてるマクロ</a>はCommon Lispのマクロに比べると遥かに貧弱で、<a href="http://www.seshop.com/product/detail/12043/">PAIP</a>で紹介されてる実装のスペックには届きませんでした。その一つとして継承機能が組み込めなかった。<br />
もっともPAIPで紹介されてるOOPマクロが継承可能なのは、中で使われてるmessage変数が<a href="http://clhs.lisp.se/Body/f_gensym.htm">gensym</a>されてないんで、そこに子クラスの条件ツッコむとcase内の選択肢が増える、と言う極めて危ない実装となっています(笑)。それで考えるとSchemeのマクロが安全なんですけどね。<br />
それから、PAIPの実装だと、クラス定義した時点でメソッドが自動で定義されるんですが、これがR5RS Scheme マクロだと不可能でした。そこでしょーがないんで、外部的にdefine-methodと言うマクロを作って、それでメソッド名をわざわざ打ち込む形式にせざるを得ませんでした。CLOSと普通のオブジェクト指向の間の子のようなスタイルになってしまいましたね。<br />
あと、Peter Norvigによる実装ですと、クラス変数がdefunを包んでるように定義されていて(クロージャで囲まれてる)、このスタイルだとやはりそのままSchemeでは実現出来ません(Schemeだとこの場合ローカル関数扱いになってクラスが外から全く見えなくなってしまう)。そこでPythonのようなクラス変数へのアクセスも考慮して、思い切ってクラス変数は大域変数としてクラス外で連想リストとして定義して、そこへのアクセッサをget-class-var、class-var-set!として定義しています。ちと不格好なんですが、前回見たとおり、使いドコロによっては便利ですが同時に危険なクラス変数へのアクセスは明示的に不便な薫りを漂わせておくべきだ、ってのは結果悪くないんじゃないか、とは思います。<br />
ちなみに、連想リストを使ってる+Racketの厳密なクオート適用のせいで、<a href="http://norvig.com/paip/clos.lisp">原作</a>ではフツーの関数だったけど、ここではマクロにせなあかんかったのが何個かあります。<br />
<br />
<br />
テスト実行は以下のとおりです。<br />
<br />
<script src="https://gist.github.com/anonymous/8231777.js"></script>
<br />
<br />
<br />
と言うわけで、ちと不格好なOOPモドキではありますが、これらを駆使してScheme版「イスカンダルのトーフ屋ゲーム」を書いていこうと思います。<br />
<br />
<b>Parser クラス (Read)</b><br />
<br />
さて、前回は結構勢いだけで作っていって、「どう言う順番で組み立てていくべきか」とかあんま考えてなかったんですよね(笑)。結構行き当たりばったりでした(笑)。<br />
まあ、今回は二回目ですし、また参考に出来るPythonのコードもあるし、ってな事もあるんですが、しかしそれでもどうやらやはり<br />
<br />
<blockquote class="tr_bq">
REPL構造はRead->Eval->Printの順序で組み立てていくべきだ</blockquote>
<br />
って事がちと分かりましたね。<br />
考えてみれば当たり前なんですが、<b>情報の流れを追った順序で組み上げていくのが原則</b>で、逆方向に進むとか、あるいはEvalから組み上げてみる、ってのはどうやら具合が宜しくない模様です。<br />
んで、今回もReadに関する指針は次の通りです。<br />
<br />
<br />
<ol style="text-align: left;">
<li>Perserクラスはクラス変数としてphaseを持つ。また、メソッドはinputメソッド(ここではReadと呼ぶ)のみを持つ(正確には「のみ」しか持てなかった・苦笑)。</li>
<li>Readの挙動は「何も入力が無くてもEvalに情報を渡す」「整数の入力しか受け付けないでその情報を含めてEvalに渡す」「Yes/Noに類する入力だけ受け取ってその情報を含めてEvalに渡す」の3種類とする。</li>
<li>挙動の変更はゲームのステージを表すクラス変数、phaseを参照して行う。Phaseにはゲームステージがシンボルとして束縛されている。</li>
<li>Read は2値を返す。一つ目はphaseの値をシンボルとして、二つ目は入力値、あるいは入力されない場合は #f (Schemeの偽)返す。</li>
</ol>
<div>
以上の3つです。基本的には1つ目も2つ目も前回のPython版と同じです。</div>
<div>
違う点と言うと、まずは、phaseに束縛されてる<b>シンボル</b>と言うLisp族特有のデータ型なんですが、Pythonでは文字列を返した代わりに使っています。</div>
<div>
もちろんゲームステージに対して番号ふる処理にしてもいいんですが、やっぱり組んでる人間としては「どのステージでどう言う処理を」って明示された方が分かりやすいんですよね。しかしながら、一般に<b>文字列情報は比較等の演算で時間がかかると言う欠点がある</b>んです。</div>
<div>
反面、Lisp族のデータ型シンボルとは、評価過程に於いて唯一無二と言う事が保証されていて、<b>ポインタ比較だけで何の情報が渡されたのか、とすぐ分かる</b>んですね。理論的には文字列を受け渡ししているPython版なんかより遥かに高速に比較処理してくれます。これが大きいです。</div>
<div>
しかもプログラム組んでる人間にもどう言う情報で・・・と言うのが一発で分かりますし、Lisp内に於いては<b>数値比較よりも高速</b>だったりします。至れり尽くせり、ああシンボル万歳、たぁLisp好きの合言葉ですね(笑)。</div>
<div>
あとは、3番。前回のPython版は二つの情報をまとめた「タプル」と言うデータ型でEvalとやり取りさせてたんですが、このScheme版では本当に二つ値を返しています。これも一部のLisp(特にCommon LispとScheme)では有名な不思議な機能、<b>多値関数</b>と言う機能です。手続き<a href="http://www.schemers.org/Documents/Standards/R5RS/HTML/r5rs-Z-H-9.html#%_idx_572">values</a>で包んでやるとあら不思議、いくつもの値を一気に返せるんですってよ、奥様!</div>
<div>
まあ、実際は<b>ここで使う必然性は実はあんま無かった</b>んですが(笑)、何となくやってみたかっただけです(笑)。実際ホントの事言うと、リストとか便利過ぎて、通常あんま多値の出番がないんですよね(笑)。</div>
<div>
<br /></div>
<div>
まあ、てなワケでコードを見てみますか。ぶっ飛びますよ(笑)。</div>
<div>
<br /></div>
<div>
<script src="https://gist.github.com/anonymous/8232246.js"></script>
</div>
<div>
<br /></div>
<div>
さあて、どうでしょうかこれは(笑)。<br />
まずは予告してた通り、<b>Read自体がデータ抽象として設計されてる為、一つのSchemeの手続きとしては明らかに長すぎる</b>印象があるんじゃないでしょうか。つまり、印象的には<b>何でもかんでも詰め込み過ぎてる関数に見える</b>、って事ですね。<br />
加えると、<b>PythonのOOPの場合はselfがあるんで、メソッドをクラス内でバラバラに定義してもお互いに参照可能</b>でした。一方、まあ、今回実装したクラス定義があまりに貧弱だ、って事もあるんですが(笑)、<b>クラス内で定義されたメソッドにクラス内で定義したメソッドは基本的にアクセス出来ません</b>(笑)。自分でやってて「何じゃこりゃ?」とか思ったわけですが(笑)。<br />
詳しく説明すると、<b>define-methodで定義したメソッドはインスタンスを通じてしかメソッド本体にアクセス出来ない</b>んです。<br />
<br />
<blockquote class="tr_bq">
メソッドの書式:<br />
(メソッド インスタンス 引数 ...)</blockquote>
<br />
ところが当然、今のように「クラスで定義してる最中にメソッドへのアクセスが出来ない」、つまり内部的に参照しあえない場合は・・・そうですね、メソッド内部でローカル関数として必要な機能を定義してやるしかない、って事になります(だから上のコードは<a href="http://www.schemers.org/Documents/Standards/R5RS/HTML/r5rs-Z-H-7.html#%_idx_132">letrec</a>だらけなんです)。<br />
いや、今までPythonのself嫌いで文句ばっか言ってたんですが(笑)、今回初めてPythonのself機構が好きになりましたね(笑)。自分で実際OOP作ってみて分かるその偉大さ(笑)。ああ、そう言う理由でああなんだ、とか実装してみて初めて分かる事ってあるんだなぁ、ってカンジです。<br />
もっとも、良く言われるんですが、PythonでのOOP機構は全部パブリックなんで危険だ、って話もあって、それで言うとプログラム全体で必要な機構をメソッドで外部に出して、他の内部用演算はローカル関数として外部から隠す、と言う上のような書き方の方が安全、って言えば安全かもしれません。ただし<b>見た目の酷さを除けば</b>、ですが(笑)。<br />
<br />
取り合えずテストしてみましょうか。実はこれがちょっとしたトラブルがあるんですが、まあ、大まかには予定通り動きます(笑)。<br />
<br />
<script src="https://gist.github.com/anonymous/8232677.js"></script>
とまあ、最初の方はテストが通るんですが、途中でRacketがおかしな挙動になるんですよね(苦笑)。成功する場合もあれば失敗する場合もある。なんじゃそりゃ(笑)。<br />
実はRacketの場合、(Lisp族にしては)ゴージャスなIDE付き、って事もあって、なんか特にReadで呼び出される入力機構が立派過ぎるんです(笑)。多分その関係で負荷が高く、平たく言えばこの手の入力テストやると(<a href="http://www.schemers.org/Documents/Standards/R5RS/HTML/r5rs-Z-H-9.html#%_idx_596">current-input-port</a>)なり(<a href="http://www.schemers.org/Documents/Standards/R5RS/HTML/r5rs-Z-H-9.html#%_idx_598">current-output-port</a>)のflushが上手く行かないんじゃないでしょうか。<a href="http://plt-scheme.org/">PLT Scheme</a>の頃はこんな事無かったんだけどな(苦笑)。<br />
<br />
まあ、取り合えずテストは通った事にして先に進みますか(ヲイ<br />
<br />
<b>Player Class / Computer Class / Tofu Class / Weather Class</b><br />
<br />
先ほども書いた通り、REPLは大まかにはR->E->Pの順序で組み上げて行った方が良い、と言いました。<br />
実は、結構な確率で良くある「LispでLispを作る」例ではこの原則が守られてないんですよね。と言うのも、大体のケースでは「ReadとPrintはLispのそれらを流用する」ってのが前提で、SICPやPAIPなんかの本もその全精力を「Evalの構築」に注ぎます。だから実際にREPLモデルを組む際の順序、と言うのには無頓着なんです。例えばPAIPの著者、Peter Norvigが書いた<a href="http://www.aoky.net/articles/peter_norvig/lispy.htm">このページ</a>なんかもそう言う意味ではグチャグチャで、やはり読み込むデータをどう言う風にパーズして渡すのか、決定してからEvalを書いた方が本当は実際的なんじゃないか、って思います。もっとも、本当はそうやってても、テキストを書いた時点で順番入れ替えてるのかもしれませんが。<br />
んで、Eval作成時時点でEvalで使うデータから作っていくわけです。基本的には評価機構がEvalである以上、扱うデータのその殆どは(Printで扱う文字列を除き)Eval絡みになるんじゃないでしょうか。<br />
そんなわけで、まずは(Python版と違って継承が無いので)プレイヤーを表すPlayer Classと対戦相手を表すComputer Classとをそれぞれ、そしてトーフを表すTofu Class、天候を司るWeather Classを作成します。<br />
<br />
<script src="https://gist.github.com/anonymous/8251254.js"></script>
<br />
<br />
<br />
この辺はイイですよね。ロジック的には<a href="http://beta-reduction.blogspot.jp/2013/12/blog-post.html">前回のPython版</a>とほぼ同じですし、シンプルです。フツーに構造体が使える言語でも大体近いカタチで落ち着くんじゃないでしょうか。<br />
<br />
<b>Environment Class</b><br />
<br />
で、上記で定義したクラス群を内部に保持する為のEnvironment Classを定義します。これもシンプルに定義出来ますね。<br />
<br />
<script src="https://gist.github.com/anonymous/8251336.js"></script>
<br />
<br />
<br />
これもロジック的には前回とほぼ同じなんでまぁ良いでしょう。ただし、各インスタンスにアクセスするアクセッサもここで定義しておきます。<br />
テストコードの実行結果は以下の通りです。<br />
<br />
<script src="https://gist.github.com/anonymous/8251453.js"></script>
<br />
<br />
Lispらしくテストコードでマッピングとかしてみたんですが(笑)。しかし、メソッドの性質によって、どっちがどっちにマッピングしてんだかイマイチ分かりづらいですね(笑)。構文的には逆になってるように見えます(笑)。<br />
ちなみに、例えば(get-player e)ですとこの場合、平たく言うとPlayer Classのインスタンスである、実際のplayerにアクセスしています。要するにplayer変数にアクセスしてるんですね。そしてそれが持ってるインスタンス変数を全部表示するようにして、問題無く各値にアクセス出来る事が分かります。<br />
と言うわけで、いよいよEvalの実装です。<br />
<br />
<b>Game-Master Class (Eval)</b><br />
<br />
Game-Master Class(Eval)の大まかな仕様は次の通りです。<br />
<br />
<br />
<ol style="text-align: left;">
<li>インスタンス変数として環境(env: Environment Classのインスタンス)を持つ(もう一つは、YES/NO入力に対しての挙動を変える為のStrange Flagってのをフラグとして持ってますが、これはロジック的には本質的なモノじゃないです)</li>
<li>唯一、interpメソッドを持っている。これが普通の意味ではEval本体に当たる。</li>
<li>interpは二つの引数を持つ。一つはphase情報、もう一つはReadから渡された入力値、あるいは#fである。</li>
<li>interpはphase情報に従って評価を下し、また次のphase(ゲームステージ)をParserクラスのクラス変数としてセットする。</li>
<li>interpは出力指示情報と計算結果(あれば、あるいは#f)の二つを多値として返す。</li>
</ol>
<div>
<br /></div>
<div>
とまあ、書く分にはやっぱ簡単で(笑)、特にphaseとして渡されてきたシンボルを<a href="http://www.schemers.org/Documents/Standards/R5RS/HTML/r5rs-Z-H-7.html#%_idx_114">case</a>で事業仕分け、もとい(古い・笑)適した関数呼び出せばイイんですよね。ロジックは簡単なんですが・・・。</div>
<div>
やっぱりPython的にselfで自分のメソッドを呼び出せないんで(笑)、結果としては鬼のようなローカル関数使いまくり、って形式になっちゃってます。</div>
<div>
<br /></div>
<div>
ではソースです。</div>
<div>
<br /></div>
<div>
<br /></div>
<br />
<script src="https://gist.github.com/anonymous/8258404.js"></script>
何でしょうね、これは(笑)。ローカル関数内にローカル関数があったりして、たまったモンじゃないです(苦笑)。だから言ったでしょ、フラット(インデントレベルが深くなり過ぎない構文を持つ)に書ける言語にはOOPは合いますが、そもそもこう言う「ネスト大好き!」な言語にはOOPは向かないんですって(笑)。書いた本人でも「何書いてたっけ」って迷子になりました。Python以上ですね(苦笑)。<br />
<br />
ではテストコードの実行です。<br />
<br />
<script src="https://gist.github.com/anonymous/8258894.js"></script>
<br />
コードが長い割には結果はシンプルだと言う・・・(笑)。<br />
ま、いっか(笑)。んで、返り値の二つは次のPrint クラスへと手渡されます。<br />
<br />
<b>Message Class (Print)</b><br />
<br />
さて、残るはPrint部分だけです。前回のPython版では、ロジック的には「整形表示」を司ってただけでいっちゃん簡単でした。<br />
が・・・・・・。<br />
R5RSで<a href="http://www.schemers.org/Documents/Standards/R5RS/HTML/r5rs-Z-H-9.html#%_idx_624">display</a>だけ頼りにして整形表示を自前で計算させる、って無謀な事をやっております(爆)。しまったな、SRFIから<a href="http://www.chino-js.com/ja/tech/srfi/srfi-48/srfi-48.html">format</a>引っ張ってくれば良かった(とか思っても後の祭り)。<br />
まあ、SRFIを読みこめば読み込む程実装依存度が結果高まってしまうんで(先にも書いた対応処理系自体の事情)しょーがないですか。まあ、formatさえあれば、ホントもっと短く済みます。ホントロジック的には大した事ないんです。<br />
<br />
大まかな仕様は以下の通りです。<br />
<br />
<br />
<ol style="text-align: left;">
<li>Message クラスは data と言うインスタンス変数を持ち、それはゲーム中で使う文字列からなるデータに束縛される。より正確には、文字列データをシンボルによりラベル付けされた連想リストがインスタンス変数 data となる。</li>
<li>Message クラスは唯一 Print メソッドを持つ。</li>
<li>Print メソッドは2つの引数をEvalから受け取るものとする。一つ目はどの表示をするか、と言う情報をシンボルで受け取り、二つ目は Eval が出した何らかの評価結果、あるいは #f である。</li>
<li>評価結果を利用した表示を行う(あるいは#fが入ってきた時は使う必要がない)。</li>
</ol>
<br />
以上です。簡単ですね。<br />
ではコードを。<br />
<br />
<script src="https://gist.github.com/anonymous/8259313.js"></script>
ご覧の通りです。<br />
デカくなってる理由は仕様にも書いた通り、ゲームで扱う文字列情報を連想リストとして持ってる為、また、R5RSに準じると、文字列整形用に指定子等が使えない為、ローカル関数の殆どは「整形用の演算」を行ってるのです。先に書いた用に、SRFI辺りからformatを持ってくるとかすれば殆どの処理は書かずに済むでしょう。<br />
(例えば数値表示をケツで揃える為、頭に何個スペースを入れるか、なんて計算したりして、自分でやりながら笑ってました・笑)<br />
<br />
ではテストコードです。<br />
<br />
<script src="https://gist.github.com/anonymous/8260209.js"></script>
<br />
おお、やっぱ表示が出てくると一気にゲームっぽさが出てきますね。ここに到達するまでが大変で、それまではテストコードなんか打っても無味乾燥な値が返ってくるだけですしねぇ(苦笑)。<br />
まあ、もちろん説明するべくもないでしょうが、このテストコードに引数として与えた値は、実際Evalが返すだろう事を想定した値となっています。<br />
では、いよいよREPLを組み上げます。<br />
<br />
<b>REPL(Read-Eval-Print-Loop)</b><br />
<br />
「組み上げる」とは言っても、ここまで来ると大した仕事は残ってないんですけどね。<br />
以下にREPLのコードをあげておきます。<br />
<br />
<script src="https://gist.github.com/anonymous/8260297.js"></script>
<br />
<br />
基本的には大したこたぁないです。<br />
注意点は、今回はRead、Eval、Print、の3つとも多値(2値)を返すメソッドとして実装されてるので、<a href="http://www.chino-js.com/ja/tech/srfi/srfi-11/srfi-11.html">SRFI-11</a>のlet-valuesと言う機能を用いてそれら多値を束縛して次のメソッドに渡すようにしています。<br />
まあ、一番最初の方で書きましたが、Schemeプログラミングに於いてあんま多値使う局面ってそんなに無いんで、使わないで、例えばリストなんかで結果を返しても良いんですが、一方、consは結構計算コストがあるんで、そう言う意味では多値を使う事である程度の「速度向上」は見なせる模様です。<br />
<br />
<b>イスカンダルのトーフ屋ゲーム (Scheme 版) のソースコード</b><br />
<br />
では、全部のソースコードを改めて貼っつけておきます。<br />
<br />
<script src="https://gist.github.com/anonymous/8260398.js"></script>
<br />
こう見てみると各クラスは大きいわ、長いわ、でサイテーですね(笑)。まあ、コメントなんかもありますが、600行近くあります。<br />
「Schemeらしく」関数型でプログラミングすればもっと縮まるんじゃねぇの、ってんで、実は既に関数プログラミング版作ってみたんですが、案の定 2/3 程度に圧縮されました。しかももっと綺麗(笑)。破壊的操作が一切ありません。<br />
やっぱLisp系には少なくともOOPは向かねえんじゃねぇのかなぁ。良く、グラフィックスに関しては昔はLispが優れてた、とか言いますが、OOP前提での今の世の中ですと、昔日のアドヴァンテージはあんま持てないんじゃないでしょうか。OOPで書かれたライブラリとやり取り(特に多重継承)して、CLIをGUIに簡単に変更、ってなわけにゃ行きそうもないですから。<br />
まあ、あくまで個人的にOOPをもっと理解したい、って為の実験ですし、OOP使うならPythonで書いた方がよりシンプルっぽいですね。<br />
<br />
ちなみに、今回も実行形式作ろうってトライしてみたんですが、Racketのコンパイラが文字コード周りにバグがあるらしくって、日本語でコンパイルすると作成されたexecutableがすぐ落ちちゃうんですね。<br />
そんなわけで、今回は実行形式は無し、です。</div>
</div>
cametan_001http://www.blogger.com/profile/04882440058326617382noreply@blogger.com0tag:blogger.com,1999:blog-4018043311243103708.post-47107121773913981192013-12-29T10:31:00.000+09:002014-01-05T10:27:14.441+09:00イスカンダルのトーフ屋ゲーム<div dir="ltr" style="text-align: left;" trbidi="on">
<br />
<div>
<b>スクリプトとプログラムの個人的定義</b><br />
<ol style="text-align: left;">
</ol>
</div>
<div>
良くスクリプトとプログラムって何が違うんだ、みたいな話があります。基本的にはどちらも似たようなモノなのは確かで、一般的には両方ともプログラム、です。良く</div>
<div>
<br /></div>
<div style="text-align: center;">
<blockquote class="tr_bq">
「小さい、一つの事しかしないようなものがスクリプトで、大掛かりになるとプログラム」</blockquote>
</div>
<div>
<br /></div>
<div>
なんて言われますよね。</div>
<div>
僕個人は、最近の考えだと、REPL(Read-Eval-Print-Loop)を持ってるのがプログラム、そうじゃないのがスクリプトなんじゃないか、と思ってます。</div>
<div>
つまり、端末かなんか起動して、命令して、そしてまた端末に戻るのがスクリプト、端末で起動したら終了命令が下されるまで端末上で入力を受け取って計算して結果を出す。それをずーっと行うのがプログラムなんじゃないかな、って事です。</div>
<div>
まあ、これは個人的な考えなんでそれはそれで良いでしょう。</div>
<div>
<br /></div>
<div>
<b>GUIアプリのMVCデザインパターンとREPL</b></div>
<div>
<br /></div>
<div>
GUIアプリを書こうとするととにかくややこしいです。どうにもすぐスパゲティ化してしまいます。</div>
<div>
このスパゲティ化を避ける為に色々デザインパターン、要するにプログラミングの指針が提案されていますが、その中にMVCと呼ばれるモノがあります。</div>
<div>
<br /></div>
<div>
<a href="http://ja.wikipedia.org/wiki/Model_View_Controller">Model View Controller</a></div>
<div>
<br /></div>
<div>
上のWikipediaの記事を見ると気づくのが、MVCと言うのはインタプリタの構造、REPL(Read-Eval-Print-Loop)と全く同じだと言う事です。</div>
<div>
REPLは、例えば情報工学の過程に追いて、多分、結構大学卒業年次辺りでのプログラミング言語実装課題でインタプリタを実装しましょう的なネタで使われて、平たく言うと机上の理論だし(笑)、ついでに言うとCLI(Command Line Interface)用の実装ネタでもあり、あまり実用的なネタじゃない、と思ってた(あるいは思われてる)んですが、ここに来て、インタプリタの実装経験「こそが」良いGUIアプリケーションを書く技術を養うんじゃないか、ってちょっと視点が変わってきたんですね。</div>
<div>
つまりこう言う事です。結構プログラマ(特にUNIX系)はGUIアプリが嫌い、使うのも嫌だし作るのも嫌、って人が多いみたいなんですが、指針としては</div>
<div>
<br /></div>
<div>
<ol style="text-align: left;">
<li>REPLをキチンと分離して備えたCLIアプリケーションを書く</li>
<li>GUIツールを持ち込んで多重継承を使い、Read+GUI->Controllerと言う継承、Eval+GUI->Modelと言う継承、Print+GUI->Viewと言う継承を行う</li>
</ol>
<div>
と言うプロセスを踏めば、CLIのREPLを備えたプログラムを簡単にGUI化出来るのでは、と踏んだのです。</div>
</div>
<div>
まあ、そう言うプランがあればラクなのでは、と言う予感がしてたんですが、まずは何はともあれ、CLIで「アプリケーション」と呼べる至極単純かつきちんとREPLを持つプログラムをOOPで作れないかな、ってネタを探していたわけです。</div>
<div>
<br /></div>
<div>
<b>何故OOP?</b></div>
<div>
<br /></div>
<div>
個人的にはOOPが嫌い、少なくともあまり得意じゃない、知らないことも多いし、と思ってたんですが、取り合えず一個OOPで書ければ自信に繋がるかな、と言うような事は考えてました。</div>
<div>
それはともかくとして、GUI系のツール(<a href="http://www.wxwidgets.org/">wxWidgets</a>とか<a href="http://qt-project.org/">Qt</a>とか)がまずはOOPで書かれているツールなんで、そいつらとのやり取りが絡む以上、基本的にはOOPで書かないとダメ、って事がまずあります。</div>
<div>
それと、先ほどにも書きましたが、REPLを構成するRead、Eval、Printがそれぞれクラスで定義出来たら、多重継承を使ってGUIツールが提供するクラスと融合させてModel、View、Controllerクラスを作る、と。</div>
<div>
多重継承はあまり使うべきではない、って話も聞きますが「ここ一回限り」多重継承するならそれはパワフルでシンプルにGUIアプリ作成出来る事を意味するんじゃないか。</div>
<div>
そのためにはCLIのプログラム自体がOOPで作ったパーツから組み上げられてないとダメだろう、って事ですね。</div>
<div>
<br /></div>
<div>
<b>レモネードスタンド</b></div>
<div>
とまあ、アイディアはあるんだけど、じゃあ何実装してみるか、ってのが思いつかない時期が続いていたわけです(笑)。</div>
<div>
あんまり複雑じゃなくて、でもキチンとしたアプリケーションの体裁はあって、上記のアイディアを使えるネタ、ってのが何かねぇかなぁ、と、ずーっとボーっと考えていたわけですが(笑)。<br />
んで最近、<br />
<br />
<div style="text-align: center;">
<blockquote class="tr_bq">
「あ、昔のBASICで書かれたゲームとかをPythonで実装してみりゃエエかもしんない。」</blockquote>
</div>
<br />
とか思いついたわけですね。これなら単純だからOOPに直しながら実装するにせよ、手間はいきなり本格的なアプリケーション書くよりはラクだろうし、遊べるし、昔は端末上で動いてたわけだからCLIのREPLにすることが出来る、って条件もクリアしています。<br />
それでなるたけ初期のパソコン黎明期でのBASICで書かれた単純なゲームを探しました。なおかつ、どっかでソースコードが公開されてるものがイイ。<br />
そんなんで、見つけたのは、昔のApple ][でのゲームで次のような有名なゲームです。<br />
<br />
<iframe allowfullscreen="" frameborder="0" height="344" src="//www.youtube.com/embed/0dUbDDmXp2I" width="459"></iframe>
</div>
<div>
<br />
日本では通称レモネードって呼ばれてるんですが、パソコン黎明期では非常に人気があった模様です。<br />
1988年出版の<a href="http://www.amazon.co.jp/%E3%83%86%E3%83%AC%E3%83%93%E3%82%B2%E3%83%BC%E3%83%A0%E2%80%95%E9%9B%BB%E8%A6%96%E9%81%8A%E6%88%AF%E5%A4%A7%E5%85%A8-%E3%83%86%E3%83%AC%E3%83%93%E3%82%B2%E3%83%BC%E3%83%A0%E3%83%BB%E3%83%9F%E3%83%A5%E3%83%BC%E3%82%B8%E3%82%A2%E3%83%A0%E3%83%BB%E3%83%97%E3%83%AD%E3%82%B8%E3%82%A7%E3%82%AF%E3%83%88/dp/4946432310">電視遊戯大全</a>と言う本には次のように紹介されています。<br />
<br />
<blockquote class="tr_bq">
BASIC言語で書かれた「レモネード」。<br />
日本では「イスカンダルの豆腐屋ゲーム」<br />
として紹介された。</blockquote>
ほうほうなるほど。日本で紹介されたんなら日本でソース公開されてるかもしんねぇな、とか思って探してみたんですが・・・。<br />
<br />
<b>イスカンダルのトーフ屋ゲームはレモネードスタンドじゃなかった!</b><br />
<br />
意外とアッサリと、現在でもイスカンダルのトーフ屋ゲームを公開してる作者さんの<a href="http://vivi.dyndns.org/tofu/tofu.html">サイト</a>を見つける事が出来ました。<br />
ところがここに驚くべき記述が成されてたんですね(笑)。<br />
<br />
<blockquote class="tr_bq">
当時 Apple ][ にレモネード屋さんを経営するゲームがあって、トーフ屋ゲームはそれを真似したのだろうという記述を時々みかけるが、それは間違い。おいらはそれを言われるまでレモネードゲームのことを知らなかった。(今もレモネードゲームの詳細は知らないし、たぶんプレイしたこともない)</blockquote>
何と、そうなんだ(笑)!<br />
確かに上にリンク貼ったレモネードスタンドのゲーム内容と<a href="http://vivi.dyndns.org/tofu/tofu.lzh">イスカンダルのトーフ屋ゲーム</a>は似てはいますが全く違いますね。レモネードスタンドの方がゲームとしては遥かに複雑です。<br />
違う点を上げると<br />
<br />
<br />
<ol style="text-align: left;">
<li>レモネードスタンドはレモネードの販売価格を自分で決定出来る。</li>
<li>売るための広告費をかけることが出来る。</li>
<li>対戦相手はいない。</li>
</ol>
<div>
等です。「経営戦略シミュレーション」と言うならレモネードスタンドの方で、一方、イスカンダルのトーフ屋ゲームはどっちかと言うとやっぱあくまで「対戦用」アプリです。</div>
<div>
恐らく、レモネードスタンド = イスカンダルのトーフ屋、って誤解はそれこそ、上にあげた電脳遊戯大全の記述ミスから広まったものじゃないでしょうか。</div>
<div>
ところで、どっちがプログラムとしてシンプルなのか、と言うと恐らくイスカンダルのトーフ屋ゲームの方がよりシンプルで、今回のお題には適しているようです。</div>
<div>
と言うわけで、Python2.7で、OOPで、REPLモデルとしてイスカンダルのトーフ屋を実装してみます。</div>
<div>
方針としては次のようにしました。</div>
<div>
<br /></div>
<div>
<ol style="text-align: left;">
<li>オブジェクト指向をフルに使って関数等は用いない。</li>
<li>入力と出力は完全に評価機構から切り離す。</li>
</ol>
<div>
これはtwitterなんかでオブジェクト指向な言語(例えばC++)を使いこなしてる人達からすると甚だ極端な方針らしくって、特に本当にデータ駆動型プログラミングで入出力を完全に切り離す、と言うのはあまり直感的では無い模様です。ただまあ、一応実験だし、先にも書いた「GUIアプリへのコンバート」を考えると極端でも行けるトコまで行ってみよう、と思いました。</div>
</div>
<div>
なお、イスカンダルのトーフ屋ゲームの作者さんのサイトの記述ですと、ソースが公開されてる、って書いてたんですが見当たらなかったんで、中の計算ロジックは、次の山之内案山子さんが作成された<a href="http://homepage2.nifty.com/LIPS/PUTICOM/TOFU.TXT">ソース</a>を参考にさせて頂きました。<br />
<br />
<b>イスカンダルのトーフ屋ゲームとは?</b><br />
<br />
では実際にプログラムしていく前にゲームの詳細(って程じゃないけど)を見てみましょう。<br />
原版ではゲームを開始する際にゲームの説明をするかどうか訊いてくるんですが、その文章でどんな感じのプログラムになるのか、想像が付くと思います。<br />
以下がその解説文です。<br />
<br />
<blockquote class="tr_bq">
ここはイスカンダル星。あなたはここでトーフ屋を経営し、<br />
地球への帰還費用を作り出さなくてはいけません。<br />
でもお向かいには、コンピュータが経営するトーフ屋があります。。。<br />
トーフの原価は1個40円、販売価格は50円です。<br />
1日に売れる個数は天候に左右されます。<br />
トーフは日持ちしないので、売れ残った分はすべて廃棄します。<br />
そこで、次の日の天気予報を良く見て、何個作るか決心してください。<br />
所持金5千円からはじめて早く3万円を超えた方が勝ちです。</blockquote>
<br />
とまあ、これが仕様(?)です。<br />
でははじめましょうか。</div>
<div>
<br /></div>
<div>
<b>実行: eval (GameMasterクラス)</b></div>
<br />
<br />
なお、本当はこの順番で書き上げて行ったわけではなくって、行ったり来たりかなり自分で書いてるソース内で迷子になってました(笑)。OOPはデータの割り振りが難しくってアッチコッチに飛ぶんで閉口してました。基本的には、やっぱ機能(関数やらプロシージャ)で分けて書いた方がラクだと思いますね。OOPは一筋縄じゃいきまへん。<br />
取り合えずはゲームの心臓部eval(GameMasterクラス)の定義から。<br />
<br />
<script src="https://gist.github.com/anonymous/8164655.js"></script>
</div>
<br />
実装方針としては、<br />
<br />
<ol style="text-align: left;">
<li>初期化時点でゲームに使う環境をセットする。(self.env)</li>
<li>入力自体は扱わない。仮引数xに手渡された情報を元に計算する。</li>
<li>readから仮引数xに渡される情報は現状況と入力結果(あるいはNone)の2要素のタプルとする。</li>
<li>出力自体も扱わない。出力クラスに現状況と計算結果(あるいはNone)の2要素のタプルを渡せるようにEvalを設計する。</li>
<li>各条件節で行う計算は一つ(つまり、出力クラスに渡す情報は一つ)のみに仕上げる。とにかく短くしてすぐ出力クラスに渡す。eval内でもたつかない。</li>
</ol>
<div>
とかまあ、簡単に書いてますけど、実際は紆余曲折あって泣きそうでした(笑)。</div>
<div>
最初は環境も大域変数にして、一種のフラグとして書き換える形式で作り出してたんですが、どうもOOPやってる割には綺麗じゃねえな、ってんで、最終的には環境(つまり、プレイヤー情報、対戦相手のコンピュータ情報、トーフの情報、ゲームオーバーになる得点)はこのGameMasterクラス内に内包する事で落ち着きました。</div>
<div>
また、プログラミング言語のインタプリタですと環境が重要なのはevalだけ、で、基本的にはパーズは噛ますんですが、read機構は何でも受け入れてしまうわけですね。</div>
<div>
一方、ゲームの場合、例えばこのイスカンダルのトーフ屋ゲームですが、整数値だとか、Yes/No形式の二種類の入力(と何も入力が成されない/必要ないと言う情報も合わせれば三種類)を「現在のゲームの進行状況に合わせて」切り替えないといけません。つまり、Read自体がゲームの進行状況をある程度把握してなきゃならないんです。一方、状況を把握するのは切り分けだとあくまでevalなわけで・・・。うううううむ。</div>
<div>
ってなわけで、あとでreadのコードも見せますが、read内でクラス変数としてphaseと言うフラグをセットして、evalが実行された際に「次のフェーズはこれになるよ」と言うのをreadに教える事にしています。苦肉の策です。</div>
<div>
なお、クラス変数ってのは一種の大域変数のようなモノで・・・各インスタンス共通の変数として大本を変更すれば全インスタンスも影響を受ける、と言う大変危険な変数です(笑)。しかし、ここが使いドコロで、ここでこれ出すしか無かった(苦笑)。あんまこう言うアクセス手段って綺麗じゃないんですけどね。しかしながら大域変数使いたくねぇ、ってのならこれしか手がない。</div>
<div>
<br /></div>
<div>
ちなみに、evalの各節を実行した例は次のようになっています。</div>
<div>
<br />
<script src="https://gist.github.com/anonymous/8165021.js"></script>
</div>
<div>
何が何だか分かりませんね(笑)。取り合えず2要素のタプルを基本的に返してて、第一要素は現在のゲームの状況(フェーズ)、第二要素は何らかの計算結果になってるかあるいはNoneか、ってのを把握して頂ければ良いと思います。<br />
(しかし、こうやってテスト実行するのがかなりメンドい辺りがやっぱOOPの欠点だと思う)<br />
<br />
<b>Environmentクラス</b><br />
<br />
次はevalが初期化に用いる環境クラスを定義します。<br />
ゲームも言語インタプリタと同じで、情報を環境として保持していなければなりません。環境とは、例えばこのゲームの場合、プレイヤー情報(いくらお金を持ってるか、とか)、対戦相手のコンピュータの情報、トーフの情報(製造コストはいくらか、売値はいくらか、とか)、GameOverに達するお金の額とか、ですね。<br />
これらの情報の保持は、要するに言語インタプリタに於いて、例えば実行した変数なり計算結果なりの保持であるとか、あるいはどんな関数が使えるのか、とかの記憶と全く同じです。<br />
ただし、幸いなのは、言語インタプリタと違って、例えばレキシカルクロージャを作るように仕掛けるとか、そう言った技術的に面倒な面は全く無いんで、そう言う意味ではラクです。<br />
では、Environmentクラスの定義です。<br />
<br />
<script src="https://gist.github.com/anonymous/8165059.js"></script>
初期化だけで終わるくらいに簡単な実装です。<br />
なお、UserDictionaryクラスを継承してますが、これはEnvironmentクラスのインスタンス変数を参照する際に、辞書(ディクショナリ)みたいに[]を使って参照出来てラクだろう、ってんで使っています。<br />
(しかし実際出来たコードは辞書だらけみてぇになってあまり見やすいとも思えない・・・ orz)<br />
<br />
<script src="https://gist.github.com/anonymous/8165176.js"></script>
このように、Environmentクラスのインスタンスは、辞書(ディクショナリ)データと同じようにインスタンス変数にアクセス可能です。<br />
この例では、プレイヤー、コンピュータ共に持ってる初期金額は5,000円、作ったトーフは0個、トーフの売値は50円、製造コストは40円、天候によって売れる最大個数、あとはゲームオーバーに達する目標金額は30,000円に設定されてる、って事が分かります。<br />
で、Environmentクラスにぶち込んでいるインスタンスの製造元であるクラスを見てみましょう。<br />
<br />
<b>TofuSellerクラス/Playerクラス</b><br />
<br />
<br />
まずはゲームの主体であるプレイヤーの振る舞いの基礎になるTofuSellerクラス、それとプレイヤーを演じるPlayerクラスから見てみます。<br />
<br />
<script src="https://gist.github.com/anonymous/8165236.js"></script>
TofuSellerは初期状態(初期金額5,000円、製造したトーフが0個)を保持して、何個トーフを作るか命令を出すメソッドを持っています。Playerクラスは単純にそれをそのまま継承しただけです。<br />
ちなみに、ここがOOPの難しいトコなんですが、ここではTofuSellerクラス/Playerクラスに「トーフをいくつ作るか決定する」属性を付加してるんですが、どうなんでしょうねぇ。こう言う「計算」はevalが持つべきなのか否か・・・・・・。<br />
世の中オブジェクトだらけなんで、オブジェクトで考える方が自然だ、とOOP信奉者はいいますが、なかなかどうして。こう言うデータ駆動で、「あるメソッドを何に属させるべきか決める」ってのはそんなに自明だとは思えません。<br />
例えば「計算機メソッド」ってのを定義して、これがゲームマスターが持つべきかプレイヤーが持つべきか、ってのはこれはどう考えれば良いのでしょうか。両者とも持ってるべき?しかし機能中心主義なら機能だけ定義すれば良いんで「属性」とか考えなくって済むんですよね。この辺OOP信者の言う事はちと理想論な気がします。<br />
まあ、あまり考え過ぎなきゃいい話なんですが、こだわれば厳密なトコではオブジェクトが何を意味すべきか、やっぱり分からなくなっちゃうんですね(笑)。<br />
ちなみに、ここでもインスタンス変数へのアクセスを辞書っぽく行えるようにUserDictを継承しています。<br />
<br />
<b>Computerクラス</b><br />
<br />
次は対戦相手のComputerクラスです。こいつもTofuSellerクラスを継承して作ります。<br />
<br />
<script src="https://gist.github.com/anonymous/8165399.js"></script>
makeTofuメソッドはオーバーライドします。と言うのもコンピュータは天気予報を見て自動的にいくつトーフを作るべきか計算するからですね。AIと言う程じゃあないんですが(笑)、こう言うロジックでいくつ豆腐を作るか計算します。<br />
<br />
<b>Tofuクラス</b><br />
<br />
んでお次はトーフを表すTofuクラス。何かこれが一番オブジェクトっぽいな(笑)。<br />
<br />
<script src="https://gist.github.com/anonymous/8165477.js"></script>
ここでは初期化条件として、トーフの売値、製造コスト、天候によって売れる最大数がまずはインスタンス変数として定義されていて、そしてプレイヤーの持ち金から最大いくつトーフが作れるのか返すmaximumメソッドが定義されています。<br />
まあ、このmaximumメソッドも本当はPlayer/Computerクラスが持つべきなのかevalが持つべきなのか(ry<br />
一応ここではトーフの最大個数はトーフが決める事にしております(強引)。<br />
<br />
<b>Weatherクラス</b><br />
<br />
GameOverに達する金額はわざわざクラスにする必要もないんで、そのままEnvironmentクラスに突っ込んでます。ここではもうひとつ、このゲームに重要な「天候に関する情報」を司るWeatherクラスを見てみます。<br />
Environmentクラスは初期状態ではWeatherクラスのインスタンスは持ってないんですが、ゲームが進んで行くにつれて辞書的技でWeatherクラスがGameMasterクラスのインスタンス変数(self.env['weather'])として代入されます。<br />
んで実際問題、1ターン(全フェーズ消化)につき1回、Weatherクラスのインスタンスは代入され続けるわけです。<br />
と言うのも、Weatherクラスは中に確率マシンを仕込んでまして、初期化に於いて一回「天気予報」を計算します。これは固定されて変更されません。つまり1ターン内には使いまわされるわけですね。<br />
そしてもう一つの大事な機能が「実際の天気を決定する」機能です。これは予報結果を閾値として用いてまたもや確率計算して天気を決定します。<br />
まあ、大まかにはそんなトコで、取り合えずコードを見てみましょう。<br />
<br />
<script src="https://gist.github.com/anonymous/8165703.js"></script>
これもUserDictを継承してるんで(UserDict大好きだなぁ~・笑)、天気予報の各数値(%)に辞書的形式でアクセス出来ます。メソッドreportは天気予報情報を丸ごと辞書形式で返してくれます。また、resultメソッドは実際の天気を決定して、晴れ/くもり/雨の文字列を返すようになっています。<br />
<br />
ちなみに、全フェーズとは、eval基準で見ると基本的には<br />
<br />
'input-numbers' -> 'next-day' -> 'test' -> 'input-numbers'へ戻って繰り返し<br />
<br />
と言う3フェーズの事で、Evalを延々とこの3フェーズでloopさせるわけですが、要するにこの3フェーズ内で必ず一回self.env['weather']にWeatherクラスがインスタンス変数として代入されるわけです。<br />
<br />
<b>構文解析: read(Parseクラス)</b><br />
<br />
まあ、構文解析って程じゃないんですがね。ただ、先程も書いた通り、ゲームのread機構なんで言語インタプリタには無い特徴があります。<br />
このゲームの場合、次の3つが要点です。<br />
<br />
<br />
<ol style="text-align: left;">
<li>ゲームの全てのフェーズに於いて入力が必要なわけではない。従って、多くの場合では呼び出された時点で自動的に何らかの返り値をevalに渡してゲーム(あるいはloopを)進めないとならない。</li>
<li>整数だけしか受け付けないフェーズがある。</li>
<li>YES/NO(あるいはyes/noとか)やY/N(またはy/n)しか受け付けないフェーズがある。</li>
</ol>
<div>
さて、これが一番困ったトコなんですよね。</div>
<div>
先ほども書きましたが、言語インタプリタの場合、パーズはしますが(その結果、例えば入力エラー等をユーザーに教える)、原則的には「どんな入力も」取り合えずは受け入れます。つまり、evalみたいに環境を参照する必要が無いわけです。</div>
<div>
一方、このようなゲームの場合、read自体が「フェーズによって」挙動を変えます。いや、変えないといけない。ゲームが要求する入力だけを受け、違反な入力等を過度に避ける為にはそうするしかない。従って、環境を直接参照する事が無いにせよ、何らかのカタチで「現フェーズの情報」を持ってないといけないんですね。</div>
<div>
しかしながら、フェーズ自体は原則的にevalの為のモノで、かつevalで決定されるべきモノです。果たしてevalが計算する前にreadが次のフェーズを知る事なんて出来るのか・・・・・・?</div>
<br />
これは古典的なプログラミングに於いてはまさしく大域変数で解決すべきだろう問題です。つまりフェーズ情報を大域変数として束縛しておいて、随時それを参照する・・・・・・。<br />
実はこの初期のプログラムだとそう言うカタチで作ってたんですが、どうもそれだとスッキリしなかったんですね。eval(GameMasterクラス)内部の定義が大域変数塗れになってどーにもこーにも汚ねぇな、と(笑)。しかもそんなプログラムだとOOPやってる意味が無い、とまでは言わなくても相当薄れるんですね。<br />
そこでその辺を何とか解決した(あるいはしようとした)のが次のコードです。<br />
<br />
<script src="https://gist.github.com/anonymous/8165957.js"></script>
Parserクラスでは最初にクラス変数phaseを定義しています。これがこのプログラムのキモです。<br />
クラス変数は初期状態では'introduction'に設定されてます。このクラス変数にアクセスするとこのクラスから作られた全インスタンス内のクラス変数は全部変更されるんですね。<br />
ちと試してみますか。<br />
<br />
<script src="https://gist.github.com/anonymous/8166006.js"></script>
最初にParserクラスから6つ個別にインスタンスを生成しています。OOPの理屈から言うと、この6つのインスタンスは「互いに独立じゃないとならない」前提なんですが、あら不思議。クラス変数に関して言うと親元のクラスで変更すると生成された全インスタンスのクラス変数が変更されてしまうのです(と言うか、それこそがクラス変数の「機能」なんですが)。<br />
これは見るからに「危険な機能」ですね(笑)。ある意味大域変数と同じで、そもそもOOPでクラス作成してインスタンスを生成するのは「個別のデータの独立性を高める為」な筈なんですが、このクラス変数の存在ってのはそれで言うと「綻び」です。しかし、こう言うお題の場合は役に立つ。<br />
なお、readとeval間のフェーズの伝達は次のようになってます。<br />
<br />
<br />
<ol style="text-align: left;">
<li>evalが実行される度に、「次のフェーズ」をParserのクラス変数phaseにアクセスしてセットする。</li>
<li>readは自らのクラス変数phaseを参照して挙動を変える</li>
<li>readは入力された/されないに関わらず、自らのクラス変数phaseの値と入力値/Noneの2要素をタプルとしてevalに渡す</li>
<li>evalはタプルの第一要素を見て現在のフェーズを知る</li>
</ol>
<div>
つまり、フェーズと言う情報に関してはevalとreadの間でグルグル回ってるわけです。</div>
<div>
なお、readメソッドは次の挙動を実装しています。</div>
<div>
<ol style="text-align: left;">
<li>何も受け取らずにフェーズ情報とNoneのタプルを即座に返す。</li>
<li>フェーズ情報に従って整数値だけを受け取り、フェーズ情報と入力された整数値のタプルを返す。</li>
<li>フェーズ情報に従ってYES/NO及びそれに類する文字情報だけ受け取り、フェーズ情報と真偽値のタプルを返す。</li>
</ol>
<div>
3番目の機能の実装はCommon Lispの大変<s>アホな</s>便利な関数、<a href="http://clhs.lisp.se/Body/f_y_or_n.htm">yes-or-no-p</a>の名前を借りてきてます。これはCommon Lispよろしく、入力された文字列を一旦大文字に変換して比較するようにしてますね。これって地味な関数なんですが、Common LispみたいにPythonにもデフォであったらイイのになぁ関数であります。<br />
Parserクラスのインスタンスの実行例は以下のような感じです。<br />
<br />
<script src="https://gist.github.com/anonymous/8166233.js"></script>
ご覧のように、クラス変数で与えられた情報に従って挙動が変わります。入力を必要としなかったり、あるいは入力値が期待されたモノじゃない場合、新たな(正しい)入力が来るまで待ちが入ります。</div>
</div>
<br />
<b>Messageクラス</b><br />
<br />
これで最後です。Messageクラスはゲームで表示する文章を保持し、またREPLの要素の最後の一個、Print機能を司ります。<br />
ここは基本的に出力だけなんで、文章データを含むから長いんですが、整形出力だけなんでコーディング的には大した苦労は無いパートですね。見てみましょう。<br />
<br />
<script src="https://gist.github.com/anonymous/8166319.js"></script>
個人的にはゲームで良くあるwait処理(Pythonだとtime.sleep)が絡むトコの処理がもっとシンプルに書けないのか悩んだんですが・・・この辺関数的に綺麗に書こう、ってのは難しいですね。やっぱ手続き的な仕事なんで。<br />
いずれにせよ、evalからもフェーズと何らかの計算結果(あるいはNone)のタプルがPrintメソッドに届き、そのフェーズ情報に従って必要とされる文字列を探して表示するだけ、の簡単なお仕事となっております。<br />
<br />
<b>REPL(Read-Eval-Print-Loop)</b><br />
<br />
さて、ここまでで、インタプリタ的なread、eval、printの各部品がクラスとして実装されました。あとはこの3つを組み合わせるだけで「アプリケーション」が出来上がります。Pythonスクリプト的には次のような記述で動かせるわけですね。<br />
<br />
<script src="https://gist.github.com/anonymous/8166400.js"></script>
これで原版の「イスカンダルのトーフ屋ゲーム」を上手くエミュレート出来てるんじゃないか、って思います。<br />
なお、Python版「イスカンダルのトーフ屋ゲーム」の全ソースは以下のようになっています。<br />
<br />
<script src="https://gist.github.com/anonymous/8166429.js"></script>
今回はpy2exeでWindows用のexeファイルも作ってみました。実行形式のダウンロードは以下から行って下さい。<br />
<br />
<iframe frameborder="0" height="120" scrolling="no" src="https://skydrive.live.com/embed?cid=C5297123E0B737C0&resid=C5297123E0B737C0%21180&authkey=APF-NIyNdLe_Ros" width="98"></iframe>
<br />
<br />
まあ、慣れないOOPで苦労しましたが、いずれにせよ、一応予想通り、入出力と評価部分を完全に切り分けたREPLモデルをプログラムする事は充分可能だ、って事が分かったんで、以降、GUIのプログラムを書く時が来たら、この指針を応用出来そうだ、って感触を得たのは大きいですね。</div>
</div>
cametan_001http://www.blogger.com/profile/04882440058326617382noreply@blogger.com0tag:blogger.com,1999:blog-4018043311243103708.post-86122676335600652492012-12-07T18:00:00.004+09:002012-12-07T18:00:55.270+09:00defineq<div dir="ltr" style="text-align: left;" trbidi="on">
Interlispにはdefineqと言う特殊形式がありました。これはグローバル環境で一気に関数を複数定義出来る機能です。Common Lispで言うとlabelsの大域関数版、Schemeで言うとletrecのグローバル手続き版、って言ったところでしょうか。<br />
Scheme(R5RS)でのdefineqの定義は以下の通り、です。<br />
<script src="https://gist.github.com/4231916.js?file=defineq.scm"></script>
まあ、シンプルで大したマクロじゃないですね。取り立てて役に立ちそうには見えないですし。<br />
何に使うか、みたいなのは次回で。<br />
ちなみに、SchemeのR5RSマクロでの大域環境での何らかのセッティングは、複数のテンプレを一気に変換先として設定しても上手く反映出来ません。と言うかまさしく引っかかるのは複数のdefineなんですけどね。<br />
唯一defineを、クロージャ関係無しに囲めるのはbeginだけ、なんで、それこそお約束通りに「逐次実行」のbeginで、こう言う場合は包んでしまいましょう。</div>
cametan_001http://www.blogger.com/profile/04882440058326617382noreply@blogger.com0tag:blogger.com,1999:blog-4018043311243103708.post-47509275420223011032012-11-30T05:22:00.000+09:002012-11-30T05:22:32.047+09:00Pythonで継続渡し(CPS)<div dir="ltr" style="text-align: left;" trbidi="on">
<br />
<ol style="text-align: left;">
<li><a href="http://practical-scheme.net/docs/cont-j.html">なんでも継続</a>からleaf-count/cps<script src="https://gist.github.com/4171396.js?file=leaf-count.py"></script></li>
<li><a href="http://kreisel.fam.cx/webmaster/clog/img/www.ice.nuie.nagoya-u.ac.jp/~h003149b/lang/actor/actor.html">SchemeとActor理論</a>から_fact<script src="https://gist.github.com/4171396.js?file=fact.py"></script></li>
</ol>
</div>
cametan_001http://www.blogger.com/profile/04882440058326617382noreply@blogger.com2tag:blogger.com,1999:blog-4018043311243103708.post-23458720207474167852011-05-26T22:51:00.003+09:002011-05-26T23:29:18.438+09:00おっさんにも解るwxPython<p>wxPythonでGUIアプリを作ろう第三弾です。今回のネタは既にお馴染みとなった<a href="http://op-py.blogspot.com/" >おっさんにも解るPython</a>の題材である<a href="https://sites.google.com/site/kaorinlinux/home/mikunchu.tar.gz?attredirects=0&d=1" >みくんちゅ♪セットアップヘルパ</a>を取り上げてみましょう。</p><br /><p>まあ、別に@kaorin_linux氏とやり合ったワケじゃないんですが(笑)、恐らくGUI初心者的にはwxPythonの方が扱いがラクじゃないのか、って事がありまして。Gladeの場合、GUI関係のコードはXMLに纏められてるので、実は何か書いた場合のソースコード内にはGUIの影もカタチもありません。ある意味非常にスッキリとしてるんですね。</p><br /><p>ただ、明示的にGTK+で生成されたオブジェクトを<b>たぐり寄せてこなければならない</b>と言うのは初心者的にはちょっとキツいのでは、と。総体的には<b>人間が書くコード量が増えてしまう</b>。<br /><p>加えると最終的にどう言うGUIで表示されるのか、と言うのが、コードを走らせてみないと分からないんですよ。その辺敷居が高いのでは、とちょっと気になりました。一方、wxGladeですと、今まで見てきた通り、スケルトンのプレビューを見る事は簡単です。</p><br /><p>僕的にはそのうちGTK+に移るのは全然やぶさかではないんですけど、取り敢えずWeb上には、pyGTK系関係の情報よりwxPython系の情報の方がまだ多いんですね。すなわち、自習するならwxPythonの方がラクだと言う背景もあります。</p><br /><p>まあ、そんなわけで、wxGlade + wxPythonの方が、記述量が減る、って事をお見せしようと思っています。ただ、基本的にはやっぱり極私的備忘録でありんす(笑)。</p><br /><p>なお、良く分からんのですが、<a href="http://audacious-media-player.org/" >Audacious</a>と言うアプリケーションが別途必要な模様です(何か知らんが、コイツのSkinを変える能力があるらしい)。</p><br /><br /><h3>スケルトンを作る</h3><br /><p>wxGladeの大まかな使い方は既に<a href="http://beta-reduction.blogspot.com/2011/05/pythongui.html" >第二弾</a>で見たんで大幅に端折ります(笑)。そこで主なな注意点と進め方を箇条書き的に記述していきましょう。</p><br /><ol><br /> <li><p><h4>原版ではベースはダイアログだったがwxGladeではあくまでベースはwxFrame</h4></p><br /> <p>これは大事ですね。Glade + pyGTKの場合は組み込みダイアログをいきなり呼び出して構わないようですが、残念ながらwxPythonではそれは許されません(だけでなく、実際推奨されていない模様です)。従って、<b>Application直下の子はwx.Frameでキマリ</b>です。</p></li><br /> <li><p><h4>frameのNameを"TopLevel"、Classを"MikunchApp"、Titleを"みくんちゅ♪セットアップヘルパ"にする</h4></p><br /> <p>これは良いですね。原版のコード名に合わせました。</p></li><br /> <li><p><h4>BoxSizerを使って次のようなウィジェット配置の雛形に持っていく</h4></p><br /><p><img src="http://media.tumblr.com/tumblr_llszxha9oR1qb0ini.png" /></p><br /> <p>これも良いですね。すぐ出来るでしょう。</p><br /> </li><br /> <li><p><h4>BoxSizerの一行目にStaticTextを貼り、Labelを"みくんちゅ♪セットアップヘルパ"にする</h4></p><br /><p><img src="http://media.tumblr.com/tumblr_llszykG5Rf1qb0ini.png" /></p><br /> <p>これも<a href="http://beta-reduction.blogspot.com/2011/05/pythongui.html">前回</a>同様ですね。プロパティウィンドウの[Layout]タブ => [Alignment]と辿っていって、<b>wxALIGN_CENTER_HORIZONTAL</b>にチェックを入れておけば、タイトル用ラベルが真ん中に配置されます。</p><br /> </li><br /> <li><p><h4>BoxSizerの二行目、三行目、四行目の左側にCheckBoxを配置する</h4></p><br /> <p>ここで初めて出てくるウィジェットの一つ目が<b>CheckBox</b>です。これはパレットのここにあります。</p><br /><p><img src="http://media.tumblr.com/tumblr_llszzrjsd01qb0ini.png" /></p><br /> <p>CheckBoxの配置が終われば次のようになりますね。</p><br /><p><img src="http://media.tumblr.com/tumblr_llt00abwBj1qb0ini.png" /></p><br /> <p>それぞれのCheckBoxのLabelを上から順に<b>みくつべ♪をインストール</b>、<b>みくかべ♪をインストール</b>、<b>Audaciousのスキンをインストール</b>に変更します。また、それぞれのNameを上から順に<b>chkMikutube</b>、<b>chkMikukabe</b>、<b>chkAudacious</b>にします。全部終わればデザイナは次のようになっているでしょう。</p><br /><p><img src="http://media.tumblr.com/tumblr_llt00zhCtV1qb0ini.png" /></p><br /> <p>一つだけTips。chkAudaciousのプロパティウィンドウから[Layout]タブ => [Alignment]と辿って行って、<b>wxALIGN_CENTER_VERTICAL</b>にチェックを入れておきましょう。これはこの後すぐ右側に入れる<b>ComboBox</b>との見た目のバランスを取ってくれる筈です。</p><p><img src="http://media.tumblr.com/tumblr_llt01l0XmR1qb0ini.png" /></p></li><br /> <li><p><h4>BoxSizerの四行目右側にComboBoxを配置する</h4></p><br /> <p>ここで初めて出てくるウィジェットの二つ目が、<b>ComboBox</b>です。これはパレットのここにあります。</p><br /><p><img src="http://media.tumblr.com/tumblr_llt027CfKI1qb0ini.png" /></p><br /> <p>これを配置するとデザイナは次のようになります。</p><br /><p><img src="http://media.tumblr.com/tumblr_llt02vJpjZ1qb0ini.png" /></p><br /> <p>ComboBoxのプロパティウィンドウでNameを<b>CmbAudaciousConf</b>にしてください。次に[Widget]タブに切り替えます。そこでまず、[Style]で<b>wxCB_READONLY</b>にチェックを入れます。</p><br /><p><img src="http://media.tumblr.com/tumblr_llt03yMLkV1qb0ini.png" /></p><br /> <p>そして、下に<b>Choices</b>ってのが見えると思うんですけど、<b>Add</b>ボタンを三回押して、ComboBox用の選択肢を三つ作ります。</p><br /><p><img src="http://media.tumblr.com/tumblr_llt04vGQuj1qb0ini.png" /></p><br /> <p>そして、それらの<b>Label</b>に<b>初音ミク001</b>、<b>初音ミク002</b>、<b>初音ミク003</b>と記入していきます。一つだけ注意点が。これらはUTF-8なんで日本語を認識します。が、入力に難がある。ちょっとリターンキーが「記入」と被るので変換が上手く行かないのです。そこで今与えるラベルは別にテキストエディタでも使って、コピペで貼り付けちゃいましょう。そこまで終われば次のようになってる筈です。</p><br /><p><img src="http://media.tumblr.com/tumblr_llt05j0lle1qb0ini.png" /></p><br /> <p>デザイナは次のようになってますね。</p><br /><p><img src="http://media.tumblr.com/tumblr_llt062tPUV1qb0ini.png" /></p><br /> <p>一旦プレビューもしておきますか。</p><br /><p><img src="http://media.tumblr.com/tumblr_llt06kyojX1qb0ini.png" /></p><br /> <p>いい感じですね。</p></li><br /> <li><p><h4>最下段左端にSpacerを埋める</h4></p><br /> <p>これもいいですね。SpacerをBoxSizerに置いた時にポップアップが出てきますが、サイズをwidth=240、height=30にしちゃいましょう。</p><br /><p><img src="http://media.tumblr.com/tumblr_llt075D46r1qb0ini.png" /></p><br /> </li><br /> <li><p><h4>残りのBoxSizerにButtonを置く</h4></p><br /> <p>これも良いでしょう。一つはCancelボタン(中央)、もう一つはOKボタン(右端)です。それぞれのプロパティウィンドウでNameを<b>btCancel</b>と<b>btOK</b>にします。また、[Widget]タブで<b>stockitem</b>にチェックを入れて、それぞれCancelとOKを選びます。最後に[Events]タブでそれぞれの<b>Handler</b>に<b>on_btCancel_clicked</b>、<b>on_btOK_clicked</b>と記入します。</p><br /><p><img src="http://media.tumblr.com/tumblr_llt08enUfV1qb0ini.png" /></p><br /><p><img src="http://media.tumblr.com/tumblr_llt09lv5UP1qb0ini.png" /></p><br /> <p>プレビューしてみましょうか。</p><br /><p><img src="http://media.tumblr.com/tumblr_llt0a54CwX1qb0ini.png" /></p><br /> <p>充分ですね。それではxmlファイルをパレットから[File] => [Save As...]と辿ってプロジェクトフォルダに<b>mikunchu.wxg</b>として保存します。また、Applicationのプロパティウィンドウから同じくプロジェクトフォルダをOutput pathとして、<b>mikunchu.py</b>を生成します。そして[Generate code]ボタンをクリックします。</p></li><br /></ol><br /><p>以上でスケルトン作成は終了です。</p><br /><br /><h3>まずは自動生成されたPythonコードを見る</h3><br /><p>最初にwxGladeが生成したコードを見てみます。</p><br /><script src="https://gist.github.com/993201.js?file=mikunchu_setup_helper_skelton_generated_by_wxGlade.py"></script><br /><p><a href="http://beta-reduction.blogspot.com/2011/05/pythongui.html">前回</a>見た通り、wxGladeでボタン作成をする際に、Event Handlerの名前を定義しておくとそれらHandlerのテンプレートが自動で挿入されます。ここですね。</p><br /><script src="https://gist.github.com/993201.js?file=on_btCancel_clicked_and_on_btOK_clicked.py"></script><br /><p>GUI上のボタンとこれらは既にBindメソッドによって結ばれているので、あとはこれらの定義(メソッド本体)を書き換えればボタン動作の定義は終了します。例えばCancelボタンは、みくんちゅ♪セットアップヘルパを閉じるのが目的のボタンなんで、on_btCancel_clicked()と言うメソッドは次のように書き換えれば良いわけです。</p><br /><script src="https://gist.github.com/993201.js?file=on_btCancel_clicked.py"></script><br /><p>ハナクソですね(笑)。</p><br /><br /><h3>大まかにテンプレートに付け加えるもの</h3><br /><p>まず、原版のコードを精査してみると、大まかに付け加えなければならないものが二箇所あります。まずは冒頭五行目。</p><br /><script src="https://gist.github.com/993201.js?file=import_wx.py"></script><br /><p>ここはwxだけじゃなくって、いくつか他にライブラリをインポートしないといけません。調べてみると、原版はトータルで六つライブラリをimportしてますが、そこまで必要でなく、あと二つ追加すれば何とかなる事が分かりました。</p><br /><script src="https://gist.github.com/993201.js?file=import_wx_commands_os_path.py"></script><br /><p>これで充分ですね。<a href = "http://www.python.jp/doc/2.5/lib/module-commands.html">commands</a>と言うライブラリはバックグラウンドで端末によるCLIの命令を走らせるライブラリです。つまり、ボタンをクリックした時、実際は裏で端末での処理を行わせるのが目的なのです(要するに、このアプリケーションは複雑なCLIの命令入力を隠蔽するフロントエンドです)、</p><br /><p><a href="http://www.python.jp/doc/2.4/lib/module-os.path.html" >os.path</a>と言うのは、OSのディレクトリの「場所」に関わる事を一元的に引き受けてくれるユーティリティです。</p><br /><p>もう一つ付け加えましょう。原版ではMikunchAppと言うクラス定義の前にインストールコマンド群を大域変数として定義しています。メンド臭いんで(笑)、それを全部コピーして、同じようにMikunchAppのクラス定義の前にペーストしちゃいましょう。</p><br /><script src="https://gist.github.com/993201.js?file=installCommands.py"></script><br /><h3>on_btOK_clicked()の実装</h3><br /><p>実際問題、この「みくんちゅ♪セットアップヘルパ」では、「アプリが何か行う」のはボタンがクリックされた時なんです。んで、良く考えてみるとCancelボタンに関してはとっくに実装が終わっています。っつー事はですね。極端な話、残るはon_btOK_clicked()の中身さえ定義してしまえば、このアプリの開発は終了するわけです。</p><br /><script src="https://gist.github.com/993201.js?file=on_btOK_clicked_skelton_generated_by_wxGlade.py"></script><br /><p>ここをどう書き換えるのか、ってのが実はこのアプリの肝なんです。取り敢えず原版のその部分を見てみましょうか。</p><br /><script src="https://gist.github.com/993201.js?file=original_on_btOK_clicked.py"></script><br /><p>んでまず「流れ」を把握しておきます。大きく言うと「何かして」 => 「閉じる」ってのが流れですね。「何かして」の部分は取り敢えず置いておいて、最後のgtk.main_quit()と言うメソッドでアプリケーションを「最後に閉じる」と言う事だけは確定しているわけです。これはCancelボタンの動作と基本的には同じなんです。従って、ここでもon_btOK_clickedの最後はwxPythonのアプリケーション終了命令、self.Close()が来る、と言う事が分かります。</p><br /><script src="https://gist.github.com/993201.js?file=on_btOK_clicked_with_self_Close.py"></script><br /><p>次に原版のコードを精査してみると特徴的な命令がある事が分かります。つまり、</p><br /><pre><br />self.何とか.get_active() = True:<br /></pre><br /><p>と言うパターンですね。これは一体何をやってるのか?要するにチェックボックスにチェックが入ってるのか否か、ってのを調べているんです。</p><br /><p>例えばみくかべ♪だけインストールしたい、とする。ユーザーは「みくかべ♪をインストール」だけにチェックを入れるわけです。そこで「みくんちゅ♪セットアップヘルパ」と言うアプリケーションが何をするのか?次のような「流れ」が存在するんです。</p><br /><ol><br /> <li>「みくつべ♪をインストール」がチェックされているか調べる => チェックされてた場合はインストールする、されてない場合は無視。今回はチェックされてないので無視する。</li><br /> <li>「みくかべ♪をインストール」がチェックされているか調べる => チェックされてた場合はインストールする。されてない場合は無視。今回はチェックされてるのでインストールする。</li><br /> <li>「Audaciousのスキンをインストール」がチェックされているか調べる => チェックされてた場合はインストールする。されてない場合は無視。今回はチェックされてないので無視する。</li><br /></ol><br /><p>これを上から順序良く全部行ってるんです。結構<b>バカみたい</b>でしょ(笑)?これは当然、@kaorin_linux氏がどーの、って話じゃなくって、コンピュータのプログラムってこーゆーモノなんです。上から順序良く調べて行ってる(専門的には<b>逐次処理</b>と呼びます)。ただ、人間と違って高速に処理するので、<b>一瞬で判断してるように見える</b>わけですね。</p><br /><p>また、<a href="http://op-py.blogspot.com/" >おっさんにも解るPython</a>にも記述されていますが、☓☓だったら△△する、と言う記述を<b>条件分岐</b>と呼びます。今必要なのは、「☓☓だったら」の部分で、ここは今は要するに「特定のチェックボックスにチェックが入ってたら」と言う事ですね。そして、そうなるとチェックボックスにチェックが入ってるかどうかの情報を得られないといけない。そこがpyGTKの場合は</p><br /><pre><br />self.何とか.get_active() = True:<br /></pre><br /><p>で表現されているわけです。同様の動作のwx.Python版は、</p><br /><pre><br />self.チェックボックスのインスタンス名.GetValue()<br /></pre><br /><p>です。例えば「みくつべ♪をインストール」のチェックボックスのインスタンス名はchkMikutubeなんで、</p><br /><pre><br />self.chkMikubute.GetValue()<br /></pre><br /><p>でそこにチェックが入ってるかどうか調べる事が可能です。従って、少なくとも「みくつべ♪をインストール」「みくかべ♪をインストール」の二つに関して言うと、</p><br /><script src="https://gist.github.com/993201.js?file=on_btOK_clicked_with_list_comprehension.py"></script><br /><p>で済みます。なお、@kaorin_linux氏の書き方と違って、ここでは<a href="http://www.python.jp/doc/2.4/tut/node7.html#SECTION007140000000000000000" >リスト内包表記</a>と言う書き方を用いています。何故ならその方が短く書けるから、です。それぞれ記述が一行で済む(笑)。ここもリストを返すのが目的じゃなくって、execCommand()ってのを走らせるのが目的ですね。Lisp的な言い方すると「破壊的操作」が目的で、返り値はどーでも良いわけです(笑)。ちなみにexecCommand()ってのは未定義です。<b>今から定義します</b>。</p><br /><p>ここまで分かれば、残りの部分も原版の</p><br /><script src="https://gist.github.com/993222.js?file=original_part_related_to_Audacious_skin.py"></script><br /><p>を参考にすればすぐ移植が可能です。次のようになりますね。</p><br /><script src="https://gist.github.com/993222.js?file=part_related_to_Audacious_skin.py"></script><br /><p>殆ど同じです。注意点としては、</p><br /><ol><br /> <li>get_active() == Trueの代わりはGetValue()だけで良い。== Trueは要らない。</li><br /> <li>wx.Pythonでは,ComboBox(ここではインタンス名がcmbAudaciousConf)の値を取得するには、get_active()の代わりにGetCurrentSelection()メソッドを用いる。これは選択肢の順番に従って、0から始まる値を返す。</li><br /> <li>オリジナルのコードではconfigpathは文字列結合にしているが、こっちではPythonコードの推奨の書き方でos.path.joinを用いた書き方にしてある。</li><br /> <li>editConfig()は未定義。<b>今から定義します</b>。</li><br /></ol><br /><p>つまり、on_btOK_clickedは全体的にはこうなります。</p><br /><script src="https://gist.github.com/993222.js?file=on_btOK_clicked.py"></script><br /><p>基本的にはこれで全ての実装が終了したようなモンです。ただし、未だ未定義のメソッドを二つ使ってますね。<b>execCommand()</b>と<b>editConfig()</b>の二つです。ではこの二つを実装しましょう。</p><br /><h3>execCommand()の実装</h3><br /><p>まずはexecCommand()から。っつってもこれは原版のまま流用してもらって結構です。</p><br /><script src="https://gist.github.com/993222.js?file=execCommand.py"></script><br /><p>多分これは本質的に作らなくっていいから、でしょう(笑)。恐らく、基本動作的には、commands.getoutput()だけでイイんですけど、@kaorin_linux氏がデバッグ目的で敢えてメソッドとしてラップしたんだと思います。よってこれに付いてはここまで、です(笑)。</p><br /><h3>editConfig()の実装</h3><br /><p>これも基本的には全くそのまま原版のコードを流用して構いません。と言うのもこれも別にpyGTK依存のコードじゃないから、ですね。ただ、ここではまたもや<a href="http://www.python.jp/doc/2.4/tut/node7.html#SECTION007140000000000000000" >リスト内包表記</a>を使ってコードを若干短くしてみますか。</p><br /><script src="https://gist.github.com/993222.js?file=editConfig.py"></script><br /><p>基本的なロジックはそのままです。冒頭二行はまたもやデバッグ用のprint文ですね。ここはロジックには関係ありません。</p><br /><p>@kaorin_linux氏がプログラミング初心者を念頭にコメントを記入してくれているので、基本的な「流れ」はこのままです。ただ、書き込み用リスト生成の部分でリスト内包表記を使いました。ここですね。</p><br /><script src="https://gist.github.com/993222.js?file=nested_list_comprehension.py"></script><br /><p>ここではリスト内包表記を二重に使っています。内側の[line for line in f.readlines()]ではreadlines()メソッドを用いて、行別に文字列に纏めた要素のリストを生成して、それに対してkeywordに引っかかった文字列をreplaceString + '\n'で置き換え、そうじゃない場合はそのままtxtを返して、結果リストにまとめているんですね。ちょっと外側なんて特にLisp的に見えますが(笑)、複雑な条件分岐を持つリスト内包表記では上のように論理演算子を用いるのがティピカルな手段の模様です。</p><br /><h3>wxPython版みくんちゅ♪セットアップヘルパ完成</h3><br /><p>以上で終了ですね。割にあっと言う間に終わるでしょ(笑)?では、wxPython版みくんちゅ♪セットアップヘルパの全コードを。</p><br /><script src="https://gist.github.com/993222.js?file=mikunchu.py"></script><br /><p>これは当然教材なんで、実用的には三つばかり問題点が見えました。</p><br /><p>一点目。動作的には実際は、Audacious用のスキンをインストールする場合、実は一気にインストールされるのでComboBox上の選択自体はこの点では全く関係ないわけです。つまり、設定ファイルだけを自動で書き換えたくても、「必ず」インストールが始まるんですね。ですから、本当はインストールとComboBoxの選択肢は分けた方が良いとは思います。</p><br /><p>二点目。editConfig()の大半は正規表現使えばもっとシンプルに書けるかな、とか思ったんですが失敗しました(笑)。正規表現難しいですね。全く慣れない。</p><br /><p>三点目。これもeditConfig()に纏わる話なんですけど。そもそも僕は全くAudaciousってアプリケーションを使った事が無くって(笑)。良く分からなかったんですが(笑)。この設定ファイルにそもそも"skin="とか"allow_broken_skins="って項目は無いんですよ。従って、ロジックを良く精査してみれば分かると思うんですけど、「そもそもそれらが存在しない場合」設定ファイルにこれらの条件を追加する事が出来ないんです。つまり、出来れば「存在しなかった場合」の追加条件を記述した方が良いですね。</p><br /><p>こんなカンジですかね。以上です。なお、ソースコードとwxgファイルはまた<a href="https://github.com/cametan001/mikunchu_setup_helper" >github</a>にあげてあります。</p>cametan_001http://www.blogger.com/profile/04882440058326617382noreply@blogger.com0tag:blogger.com,1999:blog-4018043311243103708.post-12023473460834816712011-05-24T09:55:00.002+09:002011-05-24T10:25:36.650+09:00PythonでGUIアプリ第二弾<p>さてと。懲りずにwxPythonでGUIアプリを作ろう第二弾です。</p><br /><p>今回も謎のエラーに随分悩まされまして、@kaorin_linux氏に助けて頂きました。ありがとうございます。いやはや、LinuxでGUIアプリを作るのはかなり敷居が高いですよ。</p><br /><p>さて、お題。今回の元ネタは<a href="http://www.learningpython.com/2006/05/30/building-an-application-with-pygtk-and-glade/">こちら</a>(日本語訳は<a href="http://po3a.blogspot.com/2006/08/pygtk-glade.html">こちら</a>)になります。今回は元ネタがWeb上に公開されてるからいいですね(笑)。</p><br /><p>紹介ページ見てもらえば分かるんですけど、元々これはwxPythonではなくってpyGTKを使ったお題なんです。が。実はちょっと記事が古くて、現状のGlade(GTK+用のGUI Builder)と整合性が無い。このページの示唆のまま進んだんですが、結局完全なGUIアプリを手に入れる事が出来ませんでした。そこでwxPythonでリベンジを目論んだわけです。</p><br /><p>今回はTreeView(wxPythonで言うListCtrl)と言う、これまたwxPython系の記事であまりお目にかからない機能を用います。まあ、ワインのリストを入力する、ってちょっとあんま役に立たなさそうなアプリなんですけど(笑)、いいでしょう。この辺クリアしておけば、今後楽しくGUIアプリを作るキッカケになるやもしれません。そうか(笑)?</p><br /><p>まあ、どっちにせよ、基本的には極私的備忘録ですからね(笑)。</p><br /><p>では早速やりましょうか。</p><br /><h3>wxGladeを使う</h3><br /><p>今回は素直に<a href="http://wxglade.sourceforge.net/">wxGlade</a>を使います。wxGladeとはwxWidgets用のGUI Buiderです。他にもwxWidgets用には<a href="http://boa-constructor.sourceforge.net/">Boa Constructor</a>なんかもあるんですが、wxGladeが一番古く、また開発が比較的活発でポピュラーなんで今回はこれを素直に用います。以下がwxGladeの画面写真ですね。</p><br /><p><img src="http://media.tumblr.com/tumblr_llnd5qrWT81qb0ini.png" /></p><br /><p>冗談です(笑)。それはともかく(笑)、3つのウィンドウが出てきますね。まずはそれぞれの名称を。</p><br /><p><img src="http://media.tumblr.com/tumblr_llnd91o6lq1qb0ini.png" /></p><br /><p>まずは<strong>パレット</strong>です。これは<strong>ウィジェット</strong>(GUI画面構成の部品)を選択するウィンドウです。wxPythonの全機能は網羅していませんが、「よく使う」部品が並べられています。ここでマウスでクリックした部品を<strong>デザイナ</strong>(後述)に配置していくわけです。</p><br /><p><img src="http://media.tumblr.com/tumblr_llndbfzSQc1qb0ini.png" /></p><br /><p>次が<strong>ウィジェットツリー</strong>です。ここでは親ウィンドウ(ウィジェットツリー上では<strong>Application</strong>と呼ばれる)を頂点としてのウィジェットの木構造が表示されます。とてもオブジェクト指向的ですね(笑)。</p><br /><p><img src="http://media.tumblr.com/tumblr_llnddfqTsb1qb0ini.png" /></p><br /><p>最後が<strong>プロパティウィンドウ</strong>です。ここでデザイナ上に配置されたウィジェットの様々な<strong>属性</strong>(つまりプロパティ)を調整します。まあ、これも全て調整出来るわけじゃあないんですが、主だったトコは調整出来るでしょう。基本的にウィジェットツリー上でクリックしたウィジェットのプロパティに従って、ここの画面が切り替わります。</p><br /><p>基本的にはこのまんま、ですね。では進みましょうか。</p><br /><h3>画面デザイン</h3><br /><p>画面デザインの最終型は以下のようなモノとします。</p><br /><p><img src="http://www.learningpython.com/images/pywine_01/small_pywine_05.png" /></p><br /><p>公式ページの通り、ですね。ではサクサク進めましょうか。</p><br /><h3>フレームとデザイナ</h3><br /><p>まずはパレットから次のアイコンをクリックします。</p><br /><p><img src="http://media.tumblr.com/tumblr_llndfh68fD1qb0ini.png" /></p><br /><p><strong>Add a Frame</strong>ですね。これがGUIデザインに於ける基本中の基本部品となります。こいつをクリックすると、</p><br /><p><img src="http://media.tumblr.com/tumblr_llndhvhDYd1qb0ini.png" /></p><br /><p><strong>Select frame class</strong>と言うポップアップが表れます。デフォルトである<strong>wxFrame</strong>になってるのを確認して[OK]ボタンを押します。そうすると、ウィジェットツリーは以下のようになり、</p><br /><p><img src="http://media.tumblr.com/tumblr_llndkki14l1qb0ini.png" /></p><br /><p>そして<strong>デザイナ</strong>が出てきます。</p><br /><p><img src="http://media.tumblr.com/tumblr_llndm9QkpU1qb0ini.png" /></p><br /><p>このデザイナ上にGUI部品(ウィジェット)を配置していくんですね。なお、<strong>sizer</strong>ってのはウィジェットをやや融通が利かないんですが、「計算通りに」配置していく為のクッションです。</p><br /><h3>プロパティウィンドウ</h3><br /><p>ここで、ウィジェットツリーで<strong>frame_1</strong>が選択されているのを確認してから、プロパティウィンドウを見てみます。</p><br /><p><img src="http://media.tumblr.com/tumblr_llndo73arY1qb0ini.png" /></p><br /><p>まずタブが三つ(Common、Widget、Code)あるのを確認してください。これがframe_1のプロパティウィンドウです。そして<strong>Common</strong>タブに於いてNameがframe_1、classがMyFrameになってるのを確認する。ここでNameをwine、ClassはpyWine、そしてSizeのチェックボックスにチェックを入れて、640, 480にしてみましょうか。</p><br /><p><img src="http://media.tumblr.com/tumblr_llndqcdIv11qb0ini.png" /></p><br /><p>ウィジェットツリーを見ると名前の変更が反映されていますね。</p><br /><p><img src="http://media.tumblr.com/tumblr_llndsq3VVA1qb0ini.png" /></p><br /><p>次にwine(pyWine)のプロパティウィンドウのタブをを<strong>Widget</strong>に切り替えます。</p><br /><p><img src="http://media.tumblr.com/tumblr_llnducmR8c1qb0ini.png" /></p><br /><p>ここでTitleをPyWineにします。そしてHas MenuBar、Has ToolBar、Has StatusBarの3つにチェックを入れます。これらはアプリケーションに、メニューバー、ツールバーステータスバー(聞きなれないかもしれませんが、ウィンドウの下に逐一情報を表示してくれる便利っ子)の三つを追加してくれます。</p><br /><p><img src="http://media.tumblr.com/tumblr_llndv84axo1qb0ini.png" /></p><br /><p>またウィジェットツリーを見てみると、sizer_1の下にwine_menubar、wine_toolbar(wxToolBar)、wine_statusbarの三つが追加されてるのが分かります。</p><br /><p><img src="http://media.tumblr.com/tumblr_llndwnc1Pz1qb0ini.png" /></p><br /><blockquote><br /><h4>名前、クラス名、タイトル・・・全部似た様な意味で紛らわしいんですけど。</h4><br /><p>全くです(笑)。一応ちょっと説明しておきます。</p><br /><p>まず、Titleはウィンドウの上方にあるタイトルバーに「何を表示するか」と言う意味です。この場合はソフト名のPyWineになりますね。</p><br /><p>Classはそのままクラス名です。GUIの中心を担う部分にどう言う名前を付けるのか、と言う事です。</p><br /><p>Name、と言うのは今から作るクラスのインスタンスのエイリアス、平たく言うと<strong>変数名</strong>です。でも何でこんなのいきなり付けるんでしょうか?</p><br /><p>と言うのも、wxGladeが作るPythonファイルの雛形は、スクリプトの形式を取っています。つまり例の悪名高い(笑)'__main__'が絡んできます。ここでmainloop()を行うインスタンスが代入された変数名をどうするのか、と言うのがここで言うNameの役割なんです。少なくとも、スクリプトファイルとして実行する場合、どう言う変数名でプログラム全体が参照されるのか、って事ですね。</p><br /><p>つまり、Pythonの名前空間を考えた場合、Title = ClassはOK、Title = NameもOKなんですけど、Class = Nameは非常に危険です。名前の衝突が起きる可能性がある。少なくとも、ClassとNameは別々になるように名付けた方が良いでしょう。</p><br /></blockquote><br /><h3>メニューバー</h3><br /><p><img src="http://media.tumblr.com/tumblr_llndylGkD61qb0ini.png" /></p><br /><p>まずはウィジェットツリーでwine_menubarを選びます。プロパティウィンドウを見ると、wine_munubarのプロパティに切り替わってる事が分かると思います。</p><br /><p><img src="http://media.tumblr.com/tumblr_llndzzVBx31qb0ini.png" /></p><br /><p>ここで[Edit menus...]ボタンをクリックします。</p><br /><p><img src="http://media.tumblr.com/tumblr_llne1959ls1qb0ini.png" /></p><br /><p>そうするとMenu editorってのが立ち上がりますね。ここでメニューバーに色々なプルダウンメニューを追加出来るわけです。</p><br /><p>まずは[Add]ボタンをクリックして、メニューを追加します。</p><br /><p><img src="http://media.tumblr.com/tumblr_llne2yaAUv1qb0ini.png" /></p><br /><p>そしてLabelを「追加」に変更します。</p><br /><p><img src="http://media.tumblr.com/tumblr_llne3sQkPD1qb0ini.png" /></p><br /><p>もう一回[Add]ボタンを押しましょうか。</p><br /><p><img src="http://media.tumblr.com/tumblr_llne4o4Kem1qb0ini.png" /></p><br /><p>今度はLabelを「ワイン」にします。</p><br /><p><img src="http://media.tumblr.com/tumblr_llne5iMrfB1qb0ini.png" /></p><br /><p>「ワイン」が選択されたままになってるのを確認して、[>]ボタンを押します。そうすると「ワイン」が一段右にズレるのが確認出来るでしょう。</p><br /><p><img src="http://media.tumblr.com/tumblr_llne6gay881qb0ini.png" /></p><br /><p>これは「ワイン」が、「追加」の子になった事を意味します。分かりにくければ、要するに、プルダウンメニューとして「追加」があって、その中に「ワイン」と言う選択項目がある、と言う意味です。</p><br /><p>次に、再び「ワイン」が選択されている事を確認して、右側にある<strong>Event Handler</strong>に<strong>on_AddWine</strong>と記述します。</p><br /><p><img src="http://media.tumblr.com/tumblr_llne7pmzag1qb0ini.png" /></p><br /><p>そこまで終わったら[適用]ボタンを押して[OK]を押しましょう。その後、ウィジェットツリーからwine(pyWine)を右クリックして<strong>Preview(wine)</strong>を選びます。</p><br /><p><img src="http://media.tumblr.com/tumblr_llnec7kA1x1qb0ini.png" /></p><br /><p>そうすると、プレビューが現れます(これが残念ながらGladeには存在しない、wxGladeの優れたトコロです)。ウィンドウ上方に[追加]があって、メニューに[ワイン]があるのが確認出来るでしょう。このように、プレビューはウィジェットツリーのApplicationの直接の子(もちろん複数あり得ますが)で確認出来るので、GUI画面作成に於いて活用しましょう。</p><br /><p><img src="http://media.tumblr.com/tumblr_llnednJcsV1qb0ini.png" /></p><br /><p>なお、オリジナルではメニューバーに[ファイル]、[編集]、[表示]、[ヘルプ]も存在しますが、ここでは端折ります。これはGladeで作成するとデフォルトで追加されるんですが、生憎wxGladeではそうはなりません。つまりメンド臭いんで端折ります(笑)。</p><br /><p>ただ、これらに関してはチュートリアルがあるので(例えば<a href="http://plaza.rakuten.co.jp/kugutsushi/diary/201010020000/">ここ</a>と<a href="http://plaza.rakuten.co.jp/kugutsushi/diary/201010060001/">ここ</a>と<a href="http://plaza.rakuten.co.jp/kugutsushi/diary/201010060002/">ここ</a>と<a href="http://plaza.rakuten.co.jp/kugutsushi/diary/201010090000/">ここ</a>と<a href="http://plaza.rakuten.co.jp/kugutsushi/diary/201010090002/">ここ</a>と<a href="http://plaza.rakuten.co.jp/kugutsushi/diary/201010090003/">ここ</a>等)それを参照すればすぐ実装出来ると思います。</p><br /><h3>ツールバー</h3><br /><p>次はツールバーに取り掛かります。先ほどと同じように、今度はウィジェットツリーでwine_toolbar(wxToolBar)を選択、そしてプロパティウィンドウを見ます。</p><br /><p><img src="http://media.tumblr.com/tumblr_llnehaS6d91qb0ini.png" /></p><br /><p>一番下に[Edit tools...]ボタンがあるんで、そいつをクリックすると<strong>Toolbar editor</strong>が立ち上がります。</p><br /><p><img src="http://media.tumblr.com/tumblr_llneis3wdW1qb0ini.png" /></p><br /><p>[Add]ボタンを押して、itemを追加。Labelを「Add Wine」にする。Event Handlerを再び「on_AddWine」にします。</p><br /><p><img src="http://media.tumblr.com/tumblr_llnekcF0j61qb0ini.png" /></p><br /><p>次に、Normal Bitmapから(Ubuntuだったら)</p><br /><pre>/usr/share/icons/gnome/24x24/actions/gtk-add.png<br /></pre><br /><p>と言うパスを選びます。他のLinuxディストロだったらどうなんでしょうね(笑)。あるいはWindowsだったら(笑)。知りません(笑)。まあ、今回はUbuntuのデフォルトアイコンを使ってますが、ひょっとしたら画像フォルダを自分で作って、そこに自作アイコンを突っ込んで、そいつを参照した方が良いかもしれませんね。ええと、ちなみに画像サイズは24 x 24くらいみたいです。</p><br /><p><img src="http://media.tumblr.com/tumblr_llneliElgF1qb0ini.png" /></p><br /><p>また[適用]をクリックして[OK]をクリック。そしてウィジェットツリーのwine (pyWine)を右クリックしてプレビューしてみますか。</p><br /><p><img src="http://media.tumblr.com/tumblr_llnemv48Oh1qb0ini.png" /></p><br /><p>いい感じですね(笑)。ソフトウェアらしい見た目になってきました(笑)。</p><br /><h3>リストコントロール</h3><br /><p>さて、今までほったらかしてきたデザイナですが(笑)、現在こんな状況になっております。</p><br /><p><img src="http://media.tumblr.com/tumblr_llnent59kt1qb0ini.png" /></p><br /><p>いつの間にやら[追加]メニューやらツールバーが生えております(笑)。</p><br /><p>ところで中央の灰色の部分ですが、もう一度言うとこいつが<strong>Sizer</strong>です。ウィジェットを配置する為のクッションですね。ウィジェットは必ずコイツの上に乗せる、ってのがwxGladeのルールです。</p><br /><p>今回何を乗せるか。それはリストコントロール、と言います。GTK+ではTreeView等と呼ばれてるみたいですが、元々はリスト何とやら、と言う呼び名だった模様です。どんどん複雑化していって、TreeViewに枝分かれしたみたいですね。wxWidgetsでは古典的な名称を採用している模様です。</p><br /><p>そのリストコントロールはパレットのここにあります。</p><br /><p><img src="http://media.tumblr.com/tumblr_llner4Uoqp1qb0ini.png" /></p><br /><p>右から1列目、上から4行目ですか。って言い方好きじゃないんですけどね、ホントは。wxGladeもヴァージョン上がるとボタンの配置がチョコチョコ変わったりするみたいなんで。</p><br /><p>いずれにせよ、そいつをクリックした後、デザイナのSizer部分をクリックします。</p><br /><p><img src="http://media.tumblr.com/tumblr_llnesd13ob1qb0ini.png" /></p><br /><p>Sizerにリストコントロールが乗りました。そいつを確認したらリストコントロールのプロパティウィンドウを確認してみますか。</p><br /><p><img src="http://media.tumblr.com/tumblr_llneumU72n1qb0ini.png" /></p><br /><p>リストコントロールにはタブが5つ(Common、Layout、Widget、Events、Code)あります。でも今回は特に弄る必要がないんで、CommonのNameだけを<strong>wineView</strong>に変更しましょう。</p><br /><p><img src="http://media.tumblr.com/tumblr_llnevnUvRk1qb0ini.png" /></p><br /><p>ウィジェットツリーのListCtrl_1もwineViewに変更されましたね。</p><br /><p><img src="http://media.tumblr.com/tumblr_llnewvJDn91qb0ini.png" /></p><br /><h3>ダイアログの作成</h3><br /><p><a href="http://www.learningpython.com/2006/05/30/building-an-application-with-pygtk-and-glade/">元ページ</a>には次のように書かれています。</p><br /><blockquote><em>それで、次にするのは、ユーザが新しいワインを追加するためのダイアログを作成することだ。ここではシンプルに、ワインの名前と、ワイン醸造所(ワイナリー)、製造年、あとブドウの種類を入力できるようにしよう。</em></blockquote><br /><p>よしなに。</p><br /><p>まずはダイアログのデザインから。</p><br /><p><img src="http://www.learningpython.com/images/pywine_01/pywine_04.png" /></p><br /><p>これを目標にやっていきましょうか。</p><br /><p>先ほどからの続きです。そのままで構いません。まずはパレットから<strong>Add a Dialog/Panel</strong>を選びます。</p><br /><p><img src="http://media.tumblr.com/tumblr_llnexpj0QE1qb0ini.png" /></p><br /><p>こいつをクリックすると<strong>Select widget type</strong>と言うポップアップが現れます。</p><br /><p><img src="http://media.tumblr.com/tumblr_llnf1cCDmc1qb0ini.png" /></p><br /><p>取り敢えずデフォルトのままでいいですか。[OK]ボタンをクリックしましょう。そうするとウィジェットツリーは次のようになります。</p><br /><p><img src="http://media.tumblr.com/tumblr_llnf2wdDK21qb0ini.png" /></p><br /><p>先ほど作ったwine (pyWine)と同じ階層です。つまり、両者ともApplicationを親に持つ直接の子だ、って事になりますね。</p><br /><p>ついでにダイアログ用のデザイナも現れると思います。</p><br /><p><img src="http://media.tumblr.com/tumblr_llnf41uQWS1qb0ini.png" /></p><br /><p>では、ダイアログのプロパティウィンドウを見てみます。</p><br /><p><img src="http://media.tumblr.com/tumblr_llnf5delIJ1qb0ini.png" /></p><br /><p>タブはCommon、Widget、Codeの三つですね。Common内のNameとClassを共に<strong>wineDialog</strong>にします。</p><br /><p><img src="http://media.tumblr.com/tumblr_llnf6qrqC41qb0ini.png" /></p><br /><p>WidgetタブでTitleを<strong>Add Wine</strong>に変更します。</p><br /><p><img src="http://media.tumblr.com/tumblr_llnf7cNXTS1qb0ini.png" /></p><br /><h3>Sizerを使う</h3><br /><p>ではAdd Wineをデザインしていきましょうか。それには<strong>Sizer</strong>を使います。まずはAdd Wineを上下二分割しましょう。</p><br /><p>パレットから<strong>Add a BoxSizer</strong>を選びます。</p><br /><p><img src="http://media.tumblr.com/tumblr_llnf8ugRfe1qb0ini.png" /></p><br /><p>こいつをクリックして、Add Wineのデザイナをクリックすると、次のようなポップアップが現れます。</p><br /><p><img src="http://media.tumblr.com/tumblr_llnfecPElZ1qb0ini.png" /></p><br /><p><strong>Orientation</strong>とは方向の事で、左右に分割したい場合は<strong>Horizontal</strong>、上下に分割したい場合には<strong>Vertical</strong>を選びます。今は上下に分割したいので、Verticalを選びます。また、<strong>Slots</strong>ってのは分割数ですね。繰り返しますが、上下二分割なんで2にします。</p><br /><p><img src="http://media.tumblr.com/tumblr_llnffaxy9Z1qb0ini.png" /></p><br /><p>[OK]ボタンをクリックするとAdd Wineのデザイナは次のようになります。</p><br /><p><img src="http://media.tumblr.com/tumblr_llnfg7a9xE1qb0ini.png" /></p><br /><p>上下二分割になりましたね。次は上半分を4行2列に分割したい。こう言う場合は<strong>Add a Grid Sizer</strong>を使います。</p><br /><p><img src="http://media.tumblr.com/tumblr_llnfh2BERs1qb0ini.png" /></p><br /><p>こいつをクリックした後、Add Wineのデザイナの上半分をクリックすると、次のようなポップアップが現れます。</p><br /><p><img src="http://media.tumblr.com/tumblr_llnfikDi9f1qb0ini.png" /></p><br /><p><strong>Rows</strong>(行)に4を入力、<strong>Cols</strong>(列)に2を入力した後、[OK]ボタンを押します。</p><br /><p><img src="http://media.tumblr.com/tumblr_llnfjahzE21qb0ini.png" /></p><br /><p>するとAdd Wineのデザイナは次のようになります。</p><br /><p><img src="http://media.tumblr.com/tumblr_llnfju3GHw1qb0ini.png" /></p><br /><p>次はAdd Wineの下半分をHorizontalに3分割します。これはもう出来るでしょう。最終的にこう言う状態に持って行きます。</p><br /><p><img src="http://media.tumblr.com/tumblr_llnfknwin11qb0ini.png" /></p><br /><p>何となく画面デザイン目標に近づいてきたカンジです。ちなみに、現時点ではウィジェットツリーは次のようになっています。</p><br /><p><img src="http://media.tumblr.com/tumblr_llnflobdPN1qb0ini.png" /></p><br /><h3>スタティックテキスト</h3><br /><p>では必要なウィジェットを配置していきましょう。まずは上半分から、です。最初に入力欄に対する説明の為の「ラベル」を配置します。それには、<strong>Add a StaticText</strong>を用います。</p><br /><p><img src="http://media.tumblr.com/tumblr_llnfmoXwg21qb0ini.png" /></p><br /><p>こいつをクリックして、Add Wineデザイナの上半分の左半分を埋めていきましょう。</p><br /><p><img src="http://media.tumblr.com/tumblr_llnfnmsV551qb0ini.png" /></p><br /><p>こんなカンジになりますね。ウィジェットツリーは次のようなカンジになります。</p><br /><p><img src="http://media.tumblr.com/tumblr_llnfoeUq221qb0ini.png" /></p><br /><p>ウィジェットツリーには<strong>label_1</strong>〜<strong>label_4</strong>と表示されています。そしてそれぞれのプロパティウィンドウはCommon、Layout、Widget、Codeと4つのタブを持っています。</p><br /><p>変更箇所はたった一つです。label_1〜label_4のWidgetタブ内のLabelをそれぞれ<strong>ワイン</strong>、<strong>ワイナリー</strong>、<strong>グレープの種類</strong>、<strong>製造年</strong>に変更します。そうすると、Add Wineのデザイナは次のようになってる筈です。</p><br /><p><img src="http://media.tumblr.com/tumblr_llnfpi9x9N1qb0ini.png" /></p><br /><h3>テキストコントロール</h3><br /><p>次は入力欄を作ります。そのためには<strong>Add a TextCtrl</strong>を用います。</p><br /><p><img src="http://media.tumblr.com/tumblr_llnfpsD1ah1qb0ini.png" /></p><br /><p>今度はAdd Wineデザイナの上半分の右半分にウィジェットを追加していきます。デザイナは次のようになります。</p><br /><p><img src="http://media.tumblr.com/tumblr_llnfqzbcof1qb0ini.png" /></p><br /><p>ウィジェットツリーは次のようになりますね。</p><br /><p><img src="http://media.tumblr.com/tumblr_llnfroWhk01qb0ini.png" /></p><br /><p><strong>text_ctrl_1</strong>〜<strong>text_ctrl_4</strong>のプロパティウィンドウを見ます。今度はタブがCommon、Layout、Widget、Events、Codeの5つありますが、今回はCommonタブのNameをtext_ctrl_1〜text_ctrl_4の順番で、<strong>enWine</strong>、<strong>enWinery</strong>、<strong>enGrape</strong>、<strong>enYear</strong>と名づけていきます。ウィジェットツリーに反映されて以下のようになりますね。</p><br /><p><img src="http://media.tumblr.com/tumblr_llnft3ZoJY1qb0ini.png" /></p><br /><p>一回ウィジェットツリーのwineDialogの右クリックを利用してプレビューしてみますか。</p><br /><p><img src="http://media.tumblr.com/tumblr_llnfuesxYt1qb0ini.png" /></p><br /><p>まあまあいいカンジですね。</p><br /><h3>ボタンの配置</h3><br /><p>では次はAdd Wineの下半分を何とかしましょう。まず、一番左側にはスペーサーを嵌めます。</p><br /><p><img src="http://media.tumblr.com/tumblr_llnfvzCqPO1qb0ini.png" /></p><br /><p>例によってAdd Wineデザイナの左側をクリックします。するとポップアップが現れます。</p><br /><p><img src="http://media.tumblr.com/tumblr_llnfwwRu0i1qb0ini.png" /></p><br /><p>取り敢えずデフォのままで良いです。[OK]ボタンをクリックしちゃってください。</p><br /><p>次は真ん中です。ここには[キャンセル]ボタンを嵌めたいんですが、一般的なボタンはパレットから<strong>Add a Button</strong>を選びます。</p><br /><p><img src="http://media.tumblr.com/tumblr_llnfxoynym1qb0ini.png" /></p><br /><p>ではAdd Wineデザイナに配置しましょう。</p><br /><p><img src="http://media.tumblr.com/tumblr_llnfybta871qb0ini.png" /></p><br /><p>ここで一旦、ウィジェットツリーでbutton_1と表示されている、たった今配置したボタンのプロパティを弄ります。プロパティウィンドウへ行ってください。</p><br /><p>ボタンにはCommon、Layout、Widget、Events、Codeと5つのタブがある筈です。まずCommonタブでNameをCancelに変更します。次にWidgetタブに切り替えて、stockitemにチェックを入れます。そしてリストから<strong>CANCEL</strong>を選びます。</p><br /><p>同様に、Add Wineデザイナの下半分の右側にボタンを配置します。プロパティを弄ってNameをOK、stockitemでもOKを選びます。</p><br /><p>Add Wineデザイナは次のようになってる筈です。</p><br /><p><img src="http://media.tumblr.com/tumblr_llng2zp56L1qb0ini.png" /></p><br /><p>プレビューは次のようになってる筈ですね。</p><br /><p><img src="http://media.tumblr.com/tumblr_llng3rlgM01qb0ini.png" /></p><br /><h3>ウィジェット配置のバランス調整</h3><br /><p>まあ、これで可と言えば可なんですよ。<a href="http://www.learningpython.com/2006/05/30/building-an-application-with-pygtk-and-glade/">元ページ</a>で要求している機能のスペックには到達している。</p><br /><p>ですが。</p><br /><p>見た目のバランスが悪いんですよね(笑)。果たしてこれをどーするか。</p><br /><p>恐らく日本で殆ど一番最初にwxGladeを紹介し、また現在でも参照ページとして恐らく最もアクセスが多い<a href="http://0xcc.net/pub/uu-2004-08/">このページ</a>に詳しいんですが、色々とSizerの比率やらラベルのセンタリングやら入力欄周りのピクセル数調整等をしないとなりません。んで、この辺はプログラミングじゃあないんですよね(笑)。色々パラメータ弄っては「フィーリング」で適正値らしきものを探して行かないとなりません。</p><br /><p>んで細かい説明は<a href="http://0xcc.net/pub/uu-2004-08/">先ほどのページ</a>に任しておきますが(良く書けてます)、基本的には各ウィジェットのプロパティウィンドウの<strong>Layout</strong>で調整していきます。</p><br /><p>まずはAdd Wineの上半分に対して行う事を箇条書きにしてみます。</p><br /><ol><br /><li>label_1〜label_4の<strong>Alignment</strong>の<strong>wxALIGN_CENTER_VERTICAL</strong>にチェックを入れてそれらを置かれている升目の縦方向の中央に持ってくる。</li><br /><li>入力欄それぞれの<strong>Border</strong>の値を3にする。これによって入力欄の周りに3ピクセル持たせる準備をする。</li><br /><li>入力欄それぞれの<strong>wxALL</strong>にチェックを入れ、上記を実行する。</li><br /><li>入力欄それぞれの<strong>Alignment</strong>の<strong>wxEXPAND</strong>にチェックを入れる。</li><br /></ol><br /><p>この時点でAdd Wineのデザイナは次のようになってる筈です。</p><br /><p><img src="http://media.tumblr.com/tumblr_llng5ymYAP1qb0ini.png" /></p><br /><p>プレビューは次のようになってる筈ですね。</p><br /><p><img src="http://media.tumblr.com/tumblr_llng8eY6CL1qb0ini.png" /></p><br /><p>下段に於いてやるべき事は次の事です。スペーサーのプロパティウィンドウのLayoutで、<strong>Width</strong>を100、<strong>Height</strong>を30にします。これでAdd Wineデザイナは次のようになります。</p><br /><p><img src="http://media.tumblr.com/tumblr_llng940Rk51qb0ini.png" /></p><br /><p>プレビューは次のようになります。</p><br /><p><img src="http://media.tumblr.com/tumblr_llng9p6fGw1qb0ini.png" /></p><br /><p>だいぶ良くなってはきました。ただまだ上下比率等が良くない。と言うのもSizerが画面分割する際、デフォルトでは必ず1:1の比率に分割するからです。つまり比率を変えればもっと見た目が良くなる。特に下のボタン配置スペースは結構無駄が多いですからね。</p><br /><p>これを調整するのがプロパティウィンドウのWidgetタブにある<strong>Proportion</strong>、つまりまんま比率調整です。ちなみに、これはSizerに「乗ってる側」で調整していきます。例えば最初、sizer_2でAdd_Wineを上下二分割しましたが、Proportionはsizer_2で調整出来ません。その上に乗ってるgrid_sizer_1とsizer_3のProportionの値で比率を調整します。</p><br /><p>デフォルトではgrid_sizer_1とsizer_3の比率はまんま1:1なんですが、この比率を変えていきます。まあ、この辺は個人の好みなんですが、僕がやった限り4:1辺りに持って行っても構わないカンジでした。つまり、grid_sizer_1のProportionの値を4まで上げると言う事です。これでAdd Wineのデザイナは次のようになります。</p><br /><p><img src="http://media.tumblr.com/tumblr_llngb4yRI81qb0ini.png" /></p><br /><p>ちょっとみっともないですか(笑)。でも<strong>デザイナは完全に見た目を反映しているわけではありません</strong>。ってのは実は<strong>デザイナとしては困る</strong>んじゃねーの、とも思うんですが(笑)、実際どう言うカンジで反映されてるかは、プレビューで見ないとなりません。プレビューは以下の通りですね。</p><br /><p><img src="http://media.tumblr.com/tumblr_llngbrnSZy1qb0ini.png" /></p><br /><p>なかなか良いカンジになってきています。これで良しとしましょうかね。</p><br /><h3>xmlファイルを保存する</h3><br /><p>ではデザイン情報を保存しましょう。wxGladeではデザイン情報をxmlとして保存します。ただし、通例、ファイルの拡張子は<strong>*.wxg</strong>とするようです。</p><br /><p>[File]メニューから[Save As...]を選んで、<strong>mainWindow.wxg</strong>として、そうですね、例えば~/projects/PyWineフォルダにでも保存してください。</p><br /><p><img src="http://media.tumblr.com/tumblr_llngcvwW7N1qb0ini.png" /></p><br /><h3>Python実行ファイルを出力する。</h3><br /><p>次はPythonコードを生成します。ウィジェットツリーからルートの<strong>Application</strong>を選んで、プロパティウィンドウを見ます。下の方にスクロールして、まずは<strong>Language</strong>のチェックボックスで<strong>python</strong>が選ばれている事を確認してください。</p><br /><p><img src="http://media.tumblr.com/tumblr_llngdcQQTl1qb0ini.png" /></p><br /><p>ご覧になれば分かりますが、実はwxWidgetsは他にLisp(ANSI Common Lisp)、XRC、C++、PerlでGUIコードの雛形を吐く事が出来ます。まあ、ここではPythonを使ってるんで当然PythonでGUIのコードを吐かせましょう。</p><br /><p>次にOutput pathを設定します。先ほどの~/projects/PyWineにPythonコードを吐かせましょうかね。ファイル名はpywine.pyとします。</p><br /><p><img src="http://media.tumblr.com/tumblr_llngequn081qb0ini.png" /></p><br /><p>最後にプロパティウィンドウの一番下にある[Generate code]ボタンをクリックします。以下のメッセージボックスが出てくれば無事Pythonコードが生成されています。</p><br /><p><img src="http://media.tumblr.com/tumblr_llngfhuOgY1qb0ini.png" /></p><br /><p>[OK]ボタンをクリックしてGUI画面制作に付いては全て終了です。お疲れ様でした。wxGladeを閉じましょう。</p><br /><h3>PyWineのスケルトンを実行してみる</h3><br /><p>さて、~/projects/PyWineを開くと多分次のようになっている筈です。</p><br /><p><img src="http://media.tumblr.com/tumblr_llngg9Ob7t1qb0ini.png" /></p><br /><p>Windowsならpywine.pyをダブルクリックすればGUI画面のスケルトンが立ち上がる(筈だ)と思いますし、UNIX系OSなら通常、端末から</p><br /><pre>~$ projects/PyWine/pywine.py<br /></pre><br /><p>とコマンドを打てば同様にスケルトンが立ち上がります。なお、Ubuntuでもファイルをダブルクリックするとポップアップメニューが現れて実行するか表示するか尋ねられます。</p><br /><p><img src="http://media.tumblr.com/tumblr_llnggzv1qP1qb0ini.png" /></p><br /><p>[実行する]ボタンをクリックすればスケルトンが立ち上がりますね。</p><br /><p><img src="http://media.tumblr.com/tumblr_llnghwfvpe1qb0ini.png" /></p><br /><p>ホントにスケルトンなんで、このままでは全然動作しないわけなんですけれども。まあ、GUI部分に付いては制作は確かに終了しているわけです。</p><br /><p>まあ、通常では「wxGladeの使い方」ってぇんで、このスケルトン作成だけで終了しちゃうわけなんですけれども(色々調べるのは実際メンドくせえし・笑)、ここではそうは問屋が卸しません(笑)。実際に動くトコまで持って行ってみます。少なくとも<a href="http://www.learningpython.com/2006/05/30/building-an-application-with-pygtk-and-glade/">元ネタのページ</a>が示唆しているところまでは行きましょうか。</p><br /><h3>GladeとwxGladeの違い</h3><br /><p>閑話休題。余談です。ここのセクションは実際にGladeを使った事がある人向けの話です。</p><br /><p>実はGladeは相当強力です。と言うのも、パレット部分に用意されているウィジェット配置用ボタンの数がwxGladeより遥かに多い。一方、wxGladeは相当端折ってます。wxWidgetsの主要な機能殆どを網羅している、とはとても言えません。これは大きな差で、実際問題、どんなにバックグラウンドのライブラリが優秀にせよ、GUIでのウィジェット配置の数が限られている以上、画面デザインはどんどんどんどん難しくなっていく。つまり、手書きに頼る部分が増えていく、と言うわけです。</p><br /><p>言い換えるとGUI Builderの性能は<strong>パレットに用意されたウィジェットボタンの数で決まる</strong>と言って過言じゃないんです。実はwxPythonのライブラリを眺めてたんですが、恐らくGTK+のライブラリに匹敵すると思います(と言うのも、実際問題、Linux上ではGTK+へのラッパーなので)。機能的にはね。ただし、GUIフロントエンドを持たない以上、事実上「画餅」なんです。誰も積極的に使ってみよう、とは思わないでしょう。</p><br /><p>一方、今まで見てきた通り、プレビュー機能がある、と言うのがwxGladeの最大の特徴で、これだけはGladeが逆立ちしても勝てません。そしてコードの自動生成機能がある。この二つのGUI Buiderは、両者ともxmlファイルを吐き出しますが、その扱いが大きく違うんです(両者ともウィジェット配置等の画面情報をxmlに保存しておく、って点は変わりませんが)。</p><br /><p>Gladeの場合、まずはさっきも書きましたが、プレビュー機能がない。すなわち、実際作成している画面がどうなってくのか、デザイン中には全く分からないんです。本当にWYSIWYGなんでしょうか(笑)。</p><br /><p>そして、xmlにデザイン情報を記録して(通常、*.uiファイルとして)吐き出します。ただし、これでもまだ実行ファイルは入手していません。ここがGladeの肝で、手書きでコードを作り、この*.uiを実際コードに「喰わせて」そこで初めて画面情報を解釈してGUIを表示する、と言う仕組みになってるんです。つまり、GUIアプリを作る上に於いて、xmlファイルの存在「自体」が極めて重要な鍵になっています。そして、実際にコードを手書きしてみないと、どう言った画面が現れるのかも分からないわけです。</p><br /><p>一方、wxGladeの場合、xmlファイル(つまり*.wxg)は単に画面情報を記録する媒体なだけで、作成するプログラムでこのxml自体を利用する、って事はありません。そして、スケルトンとしての目的のコードは別に吐きます。つまり、コード自体を弄ってGUIを調整するのは、xmlファイルへの依存がないので、「アリ」なんです。この辺で両者の設計思想の違いが分かります。</p><br /><p>つまり、Gladeの場合は、「手書きのコードを強要するワリには」画面デザインに関してはGladeに任せてくれ、と言う意図があるようです。ウィジェット配置なんかの情報に関して、ユーザーが直接xmlを弄って調整する、なんて事は考えていないでしょう(もちろんxmlが得意な人を除く)。</p><br /><p>一方、単なる画面情報の保存媒体としてしかxmlを考えていないwxGlade。こっちは構造的に、実行時にxmlを利用しない以上、「どーぞお好きに自動生成したコードを弄ってください」と言わんばかりです(笑)。こっちは「俺等は全機能をGUIで提供出来ねえからよお。雛形はやるからよお。後は勝手にやってちょんまげ(死語)」と言う意図が見え隠れしますね。</p><br /><p>両者とも一長一短ですね(笑)。何でこーもOSSのGUI Builderは一癖もふた癖もあるのでしょうか(笑)。正直、パレットのウィジェットボタンの豊富さと、コードの自動生成機能とプレビュー機能は全部一遍に欲しいものです(笑)。</p><br /><p>まあ、現時点両者とも、明らかにRADとしては完璧じゃないでしょう。性能的にはVSには敵わないでしょうね。あとはユーザーの「好み」の問題だと思います。</p><br /><blockquote><br /><p>注:ちなみに、最初にGladeの仕組みを聞いた時には、</p><br /><blockquote>「最初にxmlファイルを読み込ませる・・・?って事はGUI立ち上げ時にコストかかるんじゃねえの?」</blockquote><br /><p>って思いました(爆)。まあ、多分その通りでしょう。現在のマシンパワーじゃ大したコストじゃないんでしょうけどね。この仕組みを鑑みる以上、起動時にxmlのパーズが必要だ、って事でしょうから。</p><br /><p>ああ、でもGladeが元々C対応、って前提だと、「最初に全てコンパイルしちゃう」って意図なのかな。良く分かんねえや(笑)。</p><br /></blockquote><br /><h3>wxGladeが自動生成したPythonコード</h3><br /><p>ではまず、wxGladeが自動生成したPythonコードを見てみますかね。どう改造していくにせよ、雛形を最初に把握しておかなければなりませんので。ではどうぞ。</p><br /><script src="https://gist.github.com/987958.js?file=madeByWxGlade.py"></script><br /><p>良いニュースと悪いニュースがあります。どっちから先に聞きますか(笑)?</p><br /><p>では最初に悪いニュースから(笑)。<strong>コード量がメチャクチャ多い</strong>ですね(笑)。しかも自動生成コードだから読解するのが大変です。美しさの欠片もありません(笑)。だから<strong>この手の部分は隠蔽しちゃった方が良い</strong>つってるんですよ(笑)。そもそもPythonに大して詳しくもない僕が読むのは頭痛の種以外の何物でもありません。</p><br /><p>では良いニュースを。これだけのコードを吐き出してる、って事は、実はwxGladeは<a href="http://www.learningpython.com/2006/05/30/building-an-application-with-pygtk-and-glade/">元ネタのページ</a>で説明している事のほぼ殆どを実装し終わってる、って事でもあるんです。何せGladeを使ったGUIアプリ作成に於いては<strong>手書きが前提</strong>だからです。その点、wxGladeを使った我々は相当工程を短縮出来る、って意味になるんです。</p><br /><p>と言う事は後は、機能的な部分をピンポイントで実装していけば良い。<strong>キチンと上記のコードの意味を把握したら</strong>、って事ですけどね(笑)。</p><br /><p>ところで特に注目して欲しいのは次の二箇所です。まずは37行目と38行目のこの部分。</p><br /><script src="https://gist.github.com/987958.js?file=watchOut.py"></script><br /><p>もう一箇所は61行目から始まるこの部分です。</p><br /><script src="https://gist.github.com/987958.js?file=watchOut_1.py"></script><br /><p>この2箇所は何なのか?実はメニューバーとツールバーを作成した時にそれぞれで<strong>Event Handler</strong>なるモノを設定しました。両者とも<strong>on_AddWine</strong>にした。</p><br /><p>実はwxGladeでは、選択したウィジェットによっては、この通り、GUI Builder上でメニューやボタンを設定する際にEvent Handlerを指定する事によって、自動で対象メソッドへのバインドを行い、そのメソッドのテンプレートを自動作成してくれるのです。</p><br /><p>この辺も「手書きがシコシコ」が前提のGladeとは大幅に違います。Gladeの場合はスロット指定をしても、GTK+は機構的には遥かにプリミティブなんで、スロットへの通信指定や、ましてやそこと情報をやり取りするメソッドのテンプレートを用意してくれる、と言う事はあり得ません。その辺、wxGladeの方がより初心者に優しい設計にはなっています。</p><br /><p>では<a href="http://www.learningpython.com/2006/05/30/building-an-application-with-pygtk-and-glade/">元ネタのページ</a>に従い、作業をはじめましょう。</p><br /><h3>Wineクラスを作る</h3><br /><p><a href="http://www.learningpython.com/2006/05/30/building-an-application-with-pygtk-and-glade/">元ネタのページ</a>の最初の部分は大幅にスキップ出来ます(何故ならwxGladeが大幅に面倒を見てくれたから)。そこでいきなり<em>ワインの情報を保管するのに使うための Wine クラス</em>を最初に作っちゃいましょう。117行目辺り、つまり、</p><br /><script src="https://gist.github.com/987958.js?file=insertPosition.py"></script><br /><p>の間辺りに新しくクラスを作成します。</p><br /><script src="https://gist.github.com/987958.js?file=classWine.py"></script><br /><p>基本的にはオリジナルのコードのまんま、ですね。ただ、Pythonの場合は、一気に変数を同時に代入出来るので、こっちの方が好きかな。まあ、好みに依るのかもしれませんが、Lisp経験者だとこんな風に書くかも(笑)。Norvig先生、如何でしょうか(笑)。</p><br /><h3>wineDialogクラスの__init__()への変数の付け足し</h3><br /><p>さて、<a href="http://www.learningpython.com/2006/05/30/building-an-application-with-pygtk-and-glade/">オリジナル</a>だと極めてシンプルに終わってるんですが、ここが我らwxGladeがメチャクチャ複雑なコードを吐いたトコロです(笑)。比較してみましょう。</p><br /><script src="https://gist.github.com/987958.js?file=diff.py"></script><br /><p>メチャクチャ違いますね(笑)。オリジナルではほぼ何もしてない。一方、wxGlade版はダイアログに乗ってるウィジェット情報を用いてクラスを初期化しています。</p><br /><p>こう言う場合移植のポイントになるのは、オリジナルで行ってる事で、wxGlade版で行なってない事を探す事、です。幸いオリジナルが短いんで、それはすぐに見つかります。ここです。</p><br /><script src="https://gist.github.com/987958.js?file=foundTheDiff.py"></script><br /><p>一つヒントを言うと、昔のGladeだと、子ウィンドウ毎に別々のxmlファイルを吐いてたようで、要するにダイアログを出す、って事は本体と別のxmlファイルを吐く、って事を意味してたようです。って事は、僕らは"mainWindow.wxg"一つしかxmlファイルを入手していない、かつプレビューで画面が出ていた事を考えるとsetup用のgladeファイルに関する情報はまるっきり要らない、と言う話になる。</p><br /><p>って事は消去法により、上のコードしか残らないのです。んで、ここで何をやってるか、と言うとself.wineと言う変数に、先ほど作成したWineクラスのインスタンスをぶっ込んでいます。ここがwxGlade版が行なってない初期化ですね(だってさっきWineクラスを作ったばっかだし!!!)。</p><br /><p>従って、83行目辺りに次の一文をぶっこみます。</p><br /><script src="https://gist.github.com/987958.js?file=on83.py"></script><br /><p>全体としてはこうですね。</p><br /><script src="https://gist.github.com/987958.js?file=winDialogInit.py"></script><br /><p>なお、与えてる引数が違います(そもそも__init__の引数が違う)がイイんです。Wineクラスは初期化で全ての(第一引数のself以外の)引数は空文字列として初期化されています。せっかくそうしてるのに、ここでまた初期化する、ってなぁ愚の骨頂としか思えません。素直に無引数でWineクラスのインスタンスを受け取った方がなぼかマシでありんす。</p><br /><h3>runメソッドを作る</h3><br /><p>次にwineDialogクラス内にrunメソッドを作成します。なお、<a href="http://www.learningpython.com/2006/05/30/building-an-application-with-pygtk-and-glade/">原文</a>でも<em>function</em>って書かれていますが、正確には<strong>メソッド</strong>です。<strong>メソッドと関数だと全然違います</strong>。少なくともあるクラスに属させるのか、そうじゃなくってクラス外に置くべきなのか変わってくるんで、こう言う誤用は混乱を招きますね。まあ、引数に忌々しい(笑)<em>self</em>があるんで、どっちかは分かりますけど。</p><br /><p>ところでオリジナルのコードをちょっと見てみますか。</p><br /><script src="https://gist.github.com/987958.js?file=originalRun.py"></script><br /><p>まあ、大変長い(笑)。これは大変長いメソッドなんですよ(笑)。初め何やろうとしてるんだかサッパリ分かりませんでした(爆)。</p><br /><p>コメントとか見てみると、このメソッドは二つの仕事を行ってる模様です。って事は規範的なコードじゃない(笑)!!!関数型言語の人間なら既にアタマが狂ってくる作法ですね(笑)。本来なら<strong>一つの関数には一つの役割しか持たせるべきじゃない</strong>ってのが構造化を持ち出すまでもない鉄則でしょう。この原則はメソッドにも当てはまる筈です。</p><br /><p>大まかに言うと、ここのコードは</p><br /><ol><br /><li>前半は表示に関して受け持つ部分</li><br /><li>後半はダイアログで入力されたテキストを保持する部分</li><br /></ol><br /><p>の二つから成り立ってるらしい。実際問題悩んだのは、中間の</p><br /><script src="https://gist.github.com/987977.js?file=whatTheFuckIsThis.py"></script><br /><p>この辺りをどう解釈すべきか、って事だったんですけど。いずれにせよ、既にプレビューで見た通り、「表示に関する部分」はwxGladeが作成してくれている。プレビュー無しのGladeらしい作法がこの辺絡んできてるんでしょうね。表示に関する部分を作成しながら、関連する動作を(と言うか使うメソッドの関連性を重視して)恐らく一気にまとめ上げようとしたんでしょう(あまり褒められた方法論じゃないと思う・笑)。</p><br /><p>そうなると、僕らがコーディングしなきゃならないのは後半に絞る事が出来ます。と言うのも既に表示に関してはwxGladeが面倒を見てくれたからです。要するにやらなきゃならないのは<strong>ダイアログで入力された文字情報を保持する</strong>一点です。もっと技術的に言うと(使ったウィジェットを思い出す事!)テキストコントロール内に入力した文字をここで受け取る事です。</p><br /><p><a href="http://www.wxpython.org/docs/api/wx.TextCtrl-class.html">テキストコントロール</a>で入力された文字を得るには<strong>GetValue()</strong>と言うメソッドを用います。</p><br /><pre># 書式<br />GetValue(self)<br /></pre><br /><p>つまりオリジナルのコード例に従うと、</p><br /><script src="https://gist.github.com/987977.js?file=gtkWayOfGetText.py"></script><br /><p>って書くべきところを</p><br /><script src="https://gist.github.com/987977.js?file=wxPythonWayOfGetText.py"></script><br /><p>と書けば良い事になる。これだけです。</p><br /><p>と言う事はこの比較部分に於いては大して差がないんですけど、恐らく中間部分は、まずGTK+流で入力部分のオブジェクト化の明示じゃないか、って思えるんですよね。いや、詳しくは知りませんよ。どうもウィジェット情報を一々取ってるトコを見る限り、GTK+らしいプリミティブな動作をまずは明示的に行わないと、テキストを取得出来ないんじゃないか、と予想出来るのです。</p><br /><p>つまり、pyGTKだと次の3文が実はワンセットで、</p><br /><script src="https://gist.github.com/987977.js?file=GTKNeedsToHave3LinesToDoAnAction.py"></script><br /><p>一方、wxPythonだとこれがたった一文で済む、と。</p><br /><script src="https://gist.github.com/987977.js?file=wxPythonWayOfGetText.py"></script><br /><p>多分そんなトコなんじゃないか、って思います。多分、ですけどね。</p><br /><p>ところでwine.wineとかwine.wineryとか変数名が気になりますよね。僕も「何じゃこりゃ?」とか最初は思ったんですが、これは先ほどwineDialog内で定義したself.wineと言う変数から大域変数よろしく引っ張ってきたものです。</p><br /><p>一つだけ注意点を。実はPythonの<strong>クソ忌々しい文字コードに関わるトラブル</strong>がここでも出てきます。ファイル冒頭でUTF-8を指定しているにも関わらず、wxPythonのGetValue()メソッドは、何と受け取る文字をASCIIだって想定してるんですよね。んなバカな(怒)。</p><br /><p>このクソ大馬鹿Pythonを説得するには、古典的なencode('utf_8')メソッドを用いなければなりません。つまり、実際は、</p><br /><script src="https://gist.github.com/987977.js?file=youMustShowEncode.py"></script><br /><p>のように記述する必要性が出てきます。アホか、ホントにもう(笑)。</p><br /><p>従って、wxPython版のrun()メソッドは次のようになります。</p><br /><script src="https://gist.github.com/987977.js?file=runByWxPython.py"></script>目辺り、__do_layout()メソッドの後辺りにぶっ込みます。</p><br /><p>なおオリジナルのコードの最後の辺り</p><br /><script src="https://gist.github.com/987977.js?file=ignoreThese.py"></script><br /><p>は無視します(笑)。これはpyGTK系の流儀の制御法の基本らしいんですが、wxPythonでは別な方策を用いるんで、無視して結構です。取り敢えず必要なのは、返り値としてself.wineを返す事。それだけでありんす。</p><br /><h3>リストコントロールとリスト保管</h3><br /><p>まずはリストコントロールを使って列に名称を付けていきます。これはpyWineクラスの__init__メソッドに於いて行います。つまりこれも初期化の一貫ですね。</p><br /><p><a href="http://www.learningpython.com/2006/05/30/building-an-application-with-pygtk-and-glade/">オリジナルのコード</a>ではこれは次のように行われています。</p><br /><script src="https://gist.github.com/987977.js?file=isThisTheWayToDo.py"></script><br /><p>ふうむ。まずやっぱりGTK+系のライブラリは何はともあれ、まずはウィジェットを明示的に引き寄せないとならないようです。ここがそれですね。</p><br /><script src="https://gist.github.com/987977.js?file=wayOfGTKSucks.py"></script><br /><p>そうしないとリストにアイテムを突っ込めないらしい。</p><br /><p>一方、wxPythonでは同じ機能はもっと簡易に使う事が出来ます。</p><br /><pre># 書式<br />InsertColumn(self, col, heading, format, width)<br /></pre><br /><p>5つ引数がありますが、例によって<strong>selfはともかくとして</strong>必須パラメータはAddWineListColumnと同じ二個です。colでコラム(列)番号(id)を与えて、headingで列の名前を与える。</p><br /><p>さて、こうやって比較してみるとオリジナルのコードはかなり無駄があるように見えてきますね。そもそもc何とやらに数値を代入してs何とやらで列名を代入しておく。そしてそれをバラバラにAddWineListColumnに代入する……。う〜〜〜む。</p><br /><p>例えばこのような場合、最初に名称で構成されたリストなりタプルを作った方が良いのではないでしょうか。例えば以下のようにして。</p><br /><script src="https://gist.github.com/987977.js?file=shoudUseAList.py"></script><br /><p>リストもタプルも「順序」がある。と言う事は既にこれでインデックスとして成り立っている、って事です。</p><br /><p>そうすれば、あとはfor文を使って突っ込んでしまえば良い、って事です。例えばこのようにして。</p><br /><script src="https://gist.github.com/987991.js?file=iterationSample.py"></script><br /><p>この方がシンプルですよね。他にも書き方があるかもしれない。例えばこのようにする。</p><br /><script src="https://gist.github.com/987991.js?file=yetAnotherIterationSample.py"></script><br /><p>あるいは、この頃流行りの表記法、お尻の小さな表記法、こっちを向いてよ内包表記、って手を使う事も考えられます。</p><br /><script src="https://gist.github.com/987991.js?file=babyThisIsListComprehension.py"></script><br /><p>これはリストを返すのが目的なんじゃなくって、実はInsertColumnが極めて手続き的(関数型言語の言葉で言うと<strong>破壊的操作</strong>)なんで行えるんですが。要するに返り値を得る事が目的じゃない。</p><br /><p>いずれにせよ、最初の例より遥かに簡単なやり方じゃないか、と思います。これを18行目辺りに突っ込んでおきます。</p><br /><p>一旦ここで実行してみて、どう言った結果になってるか試してみようと思います。</p><br /><p><img src="http://media.tumblr.com/tumblr_llngkk8Q581qb0ini.png" /></p><br /><p>キチンと列名が表示されていますね。</p><br /><p>なお、<a href="http://www.learningpython.com/2006/05/30/building-an-application-with-pygtk-and-glade/">オリジナルのコード</a>ではこの後、実際にAddWineListColumnメソッドを作成してるんですが、僕らは組み込みメソッドで逃げたんで、 んなもん実装する必要はありません。悪しからず。</p><br /><h3>on_AddWineメソッドの作成</h3><br /><p>最後にするのは、OnAddWineメソッドの作成です。実はここが一番困って、最終的には@kaorin_linux氏に手伝ってもらった部分です。凄いですよ、彼は。コードザーッと見て間違い指摘してくれて、あっと言う間に動くトコまで持って行ってくれました。ありがとうございます。</p><br /><p>実は、EmacsとPythonってのがそれほど相性が良くなくって(ry</p><br /><p>それはともかく(笑)。@kaorin_linux氏に手伝ってもらって書いたコードは以下の通りです。</p><br /><script src="https://gist.github.com/987991.js?file=thisWasPainOnTheAss.py"></script><br /><p>まず注目して欲しいのはラストのDestroy()メソッドです。<a href="http://www.learningpython.com/2006/05/30/building-an-application-with-pygtk-and-glade/">オリジナルのコード</a>では、wineDlg側の方でDestroy()を呼んでました。基本的にオリジナルのコードではwineDialogクラス内でwineDialogを呼び出して、そして最後の処理としてDestroy()を呼び出して作業を終了しています。</p><br /><p>ところがこっちでは、On_AddWineメソッドでwineDialogのインスタンスを呼び出すようにしています。元々on_AddWineメソッドとは何なのか。それはメニュー、あるいはツールバーのボタンからワインの追加を選んだら呼び出されるメソッドでした。このメソッドが何をしなければならないのか。それは入力用ダイアログ(Add Wine)を呼び出す事です。従って、最後にダイアログを「閉じる」(要するにDestroy()する)のもOn_AddWineの役割、だと考えられますよね。そっちの方が自然だと思います。GTK+では不思議な流儀を採用している模様です(ひょっとしたら当時のXMLファイルの扱い上しょうがなかったのかもしれません)。</p><br /><p>もうひとつ不思議な名前のメソッドがあります。それはShowModal()です。基本的にはShowModal()はユーザーがダイアログ立ち上げのボタンをクリックするなり何なりした場合、ダイアログを立ち上げる機能です。しかし、立ち上げただけではダメで、ダイアログ上の、例えば「閉じる」ボタンをクリックした場合、一体どう言う返り値が来るのか、その情報を保持してくれる役割があります。</p><br /><p>つまり、ShowModal()の返り値によって行わなければならない動作がここにはあります。それは入力欄から入力された文字列のリストコントロールへの読み込みです。ここでは次の組み込みメソッドを用いてこれを行っています。</p><br /><pre># 書式<br />Append(self, entry) <br />アイテムをリストコントロールに追加する<br /></pre><br /><p>Appendはエントリとしてリストを受け取ります。リストはインデックスを持っているので、そのままリストの要素は位置番号に対応したコラム下へと挿入されます。なかなか便利ですね。</p><br /><p>いずれにせよ、もう一回整理しましょう。ここでの流れはこの通りです。</p><br /><ol><br /><li>変数wineDlgにwineDialog()のインスタンスを代入する。</li><br /><li>変数resultにwineDlg.ShowModal()の返り値を代入する。</li><br /><li>変数newWineにwineDlg.run()から得たWineクラスのインスタンスを代入する。</li><br /><li>もし、変数resultにwineDlgで[OK]ボタンがクリックされた結果が入ったら、newWineをgetListメソッドでリストに変換したあと、wineViewにAppendする。</li><br /><li>ダイアログを閉じる。</li><br /></ol><br /><p>いずれにせよ、on_AddWine()メソッドをpyWineクラスの最後、66行目辺りに付け加えます。</p><br /><p>ところでgetList()メソッドですが、これはWineクラスのメソッドとして実装します。<a href="http://www.learningpython.com/2006/05/30/building-an-application-with-pygtk-and-glade/">元ページ</a>でも次のように言ってますね。</p><br /><blockquote><em>このコードをもうちょっと読みやすくするために、wine クラスでシンプルな getList 関数を使うとこうなる</em></blockquote><br /><p>実はここでの実装では「読みやすくする」以上の理由があるんですけど、取り敢えず<a href="http://www.learningpython.com/2006/05/30/building-an-application-with-pygtk-and-glade/">オリジナルのコード</a>を見てみましょう。</p><br /><script src="https://gist.github.com/987991.js?file=originalGetList.py"></script><br /><p>一方、こちらで作成したgetList()メソッドは次のようなものです。</p><br /><script src="https://gist.github.com/987991.js?file=encodeTroubleAgainJesusKickPythonsAssMan.py"></script><br /><p>勘の良い人ならもう分かったでしょう(笑)。<strong>またもやPythonの文字コード問題</strong>です(爆)。つまり、ダイアログの入力欄から文字を受け取る時もデフォルトでASCIIだと大騒ぎされ、ここでまた、リストコントロールにAppendする際にもデフォルトでASCIIだと大騒ぎするんです(笑)。いくらファイルでUTF-8指定しても全く関係無し、です。っつーかPythonの問題と言うよりwxPythonの問題でしょうね。そこでここではそれを避ける為に、リスト内包表記でAppendに渡すリストに含まれた文字列はUTF-8だと宣言したリストを返すようにしてるんです。実用的な理由でしょ(笑)?</p><br /><p>では完成したPyWineを見てみますか。</p><br /><p><img src="http://media.tumblr.com/tumblr_llngjcIUNb1qb0ini.png" /></p><br /><p>いい感じですね(笑)。Gladeでトライした時は失敗しましたが、今回は成功です。良かった良かった。<a href="http://www.learningpython.com/2006/05/30/building-an-application-with-pygtk-and-glade/">元ページ</a>でも次のように締めています。</p><br /><blockquote><em>これで、このサンプル・アプリケーションについては終わり。情報を保存したりといったことはまだできないけど、フルの PyGTK アプリケーションの作成のための初期の概略が示せたんじゃないかと思う。</em></blockquote><br /><p>ここでも全くそう言う気持ちでいます。</p><br /><p>なおこちらでも<a href="https://github.com/cametan001/PyWine">ここ</a>でソースコードとwxgファイルを公開しています。</p>cametan_001http://www.blogger.com/profile/04882440058326617382noreply@blogger.com0tag:blogger.com,1999:blog-4018043311243103708.post-87096153980108035002011-05-24T09:16:00.005+09:002011-05-24T09:54:21.485+09:00wxPythonでハングマン<p>GUIでずーっと苦労してるんですけれども(笑)。</p><br /><p>今回、Qtからはじまって、GTK+、wxWidgetsと調べまわっていました。あっちこっちのブログなりWebページなり見て回ってたんですが。</p><br /><p>そして非常に意外だったのは、その殆どのケースでは「プログラムを作り上げる」トコまで行ってないんですね(笑)。曲がりなりにもアプリケーションを一本作り上げている、殆ど唯一の例外は我らが@kaorin_linux氏の<a href="http://op-py.blogspot.com/">おっさんでも解るPython</a>くらいで、他は殆ど、Qt CreatorなりGladeなりwxGladeなりで、ウィンドウを作るだけで終わっちゃってる、つまりGUIのスケルトンを生成する事で終わっちゃってるんです。つまり、本当に知りたい、要するに「動くプログラムを作る」トコまで到達してないんです。</p><br /><p>より正確に言うと、アプリケーションを作った例ってのはあります。ただし、それはテキストエディタだったりして。いや、テキストエディタを作ろう、ってのは悪い話じゃないです。フツーに考えれば。ただし、いまどきのGUI Buiderなんかは<strong>テキスト入力用ウィジェットくらいデフォルトで備えてる</strong>んで、これは実はウィジェットの使い方講座です。従って、ボタン貼っつけたりするのと事実上変わらないんですよ。これじゃああんま実際的なGUIのソフトウェアを書こう、と言う話までは発展しませんよね。しないんです。</p><br /><p>今回調べてみて思ったのは、設計思想としては<strong>Linux(あるいはフリーソフトウェア)のGUIの構築法は非常に優秀だ</strong>と言う事です。まあ、Windowsの方の設計理念はあんま良く知らないんですが、少なくとも、要するに<strong>GUI部分はGUIとして独立させ、CLI部分はCLI部分で書けるようにしている</strong>って事です。恐らく背景的には、歴史的にはUNIXはCLI文化であり、またCLIのプログラムを書ける人はたくさんいるんです。そこで、そう言う人達のスクリプト的な資産を即GUIアプリに転用出来るように考えてるんでしょうね。この辺、ベースとなる、例えばXや、あるいはその上に被さるQtやGTK+が意識して作られているのかもしれません。</p><br /><p>専門的には<strong>メッセージパッシング</strong>って呼ぶんですけど、OOPの根幹思考法がこれ程か、って程強調されています。つまり、GUIで組み上げたスケルトンとCLIで書かれたスクリプトが相互にやり取りするモデルとなってる。あるいは通信しあう、って事ですか。これで<strong>理論的には</strong>至極簡単にGUIアプリが書けるようにはなってるんです。</p><br /><p><strong>理論的には</strong>、ですよ(笑)。残念ながら、アッチコッチのブログなりWebページなり見て回ったんですが、実はその「通信をどうやるのか」まで行ってないんですよ(笑)。殆ど唯一の例外って言って良いのが、@kaorin_linux氏のページなんです。</p><br /><p>何でこんな状況なんですかねえ(苦笑)。一つ言えるのは<strong>Linux等のフリーソフトウェア/OSSのGUI Builderがショボ過ぎる</strong>って事があるんでしょう。つまりデザイン的に言うと、使ってる人がGUI側のコード構成なんて<strong>全く考えなくて良いように作られてない</strong>事があるのかもしれません。割に剥き出しです。つまりCLIでGUIのプログラムを書ける<strong>知識がいる</strong>。</p><br /><p>もちろん、OSS界隈ではハッカーな人がたくさんいるんでしょうし、CLIでコードを弄る方が大事なんで、中を弄れるようにしておくのが吉だ、と言う考え方もあるでしょう。でも<strong>間違ってますよ</strong>それ(笑)。次の三点でそれは間違ってる、と断言出来ます。</p><br /><p>第一に、そもそもOOPってのは<strong>中身を隠蔽出来る</strong>ってのが前提です。つまりユーザ(この場合プログラマですか)が気にしなくて良い部分は気にせんで良いシステムになってるのが前提なんです。GUIスケルトンがどんなコードで組み立てられてる、なんつーのは全く気にする必要が無い。まずこのOOPの基本原則に反している。第二にGUI Builderが提供するものはあくまでスケルトンであって、実際のトコ、これはいわゆる<strong>フロントエンド</strong>ですよね。ぶっちゃけこの部分は<strong>プログラムじゃない</strong>って考えて良いんです。プログラム本体はあくまで<strong>CLI</strong>の部分。つまり、プログラマが全力投球して組み立てなきゃならないのはCLI部分であって、GUI部分なんつーモノに時間割くのは本末転倒なんですよ。一体何の為にGUI部分とCLI本体がメッセージパッシング出来るように設計してあるのか。仮にGUIもCLIもゴッチャになってるVB的なシームレス環境ならいざ知らず、根本的な設計思想にも反してるんです。</p><br /><p>そして何より、GUI部分って書くのがつまらないんですよ(笑)。最終的な見栄え調整がGUIの、要するに役割なんですが、やってる事っつったらパーツ並べていくだけのバッチ処理が本質。大して創造性があるわけでもないですし、仮に創造性を下手に発揮したらUIの統一感がグチャグチャです。こんなもんにプログラマの知的な時間を取るべきだ、なんつーのは全くバカげてる。「それでもGUIのコードを見て弄れる自由が欲しい」なんつーのは単なるヘタなこだわりです。味噌汁でツラ洗って出直して来た方が良い(笑)。いや、マジで(笑)。</p><br /><p>つまり、Linuxを中心として提供されているGUI関係のツールってのは設計思想に反して出来が悪過ぎるんですよ。本来、GUIスケルトンとCLIのプログラムがそれぞれの役割を全うしながら通信するってモデルなら、GUI Builderは(MicrosoftのVisual Studioより)もっと直感的に使えるツールであり、吐き出されたGUI部分は全く気にせんで良いくらいじゃないといけない。要するに現状見る限り、理論先行の頭でっかちな状況なんです。そう言わざるを得ない。</p><br /><p>だから色んなWebページの作者であるとか、あるいはブログの書き手とか、恐らく元々はWinな人たちで、趣味でLinuxもやって、んで「ああ、LinuxでもGUI Builder的なモノもあるのね」と興味を持って触ってみる。で、結局、GUI Builderってのは彼らのアタマの中では当然、「簡単にGUIアプリを構築出来るモノ」じゃなきゃいけない筈なんですが、触ってみるとCLIでのGUIプログラミング知識が必要となる。何でそこまで調べなきゃアカンのだ、ああ、メンド臭くなってきた、ってなるのも分かるんですよ(笑)。だってGUI的な決まりきった定形処理する割には覚えなきゃいけないモノが多すぎるんですから。ホントメンドくせえんだもの(笑)。</p><br /><p>加えると、海外のTutorial系も似たり寄ったりなんです。まあ、確かに情報は多い。ただし、記述がGUI寄りなんですよね。分かりますかね、この表現。つまり、GUIで作成されたコードにCLIコードを埋め込みましょう、的な展開になってる。今ここまで読んでくれた方々だった「ああ、なるほどな」って合点が行くかもしれませんが、要するにGUIとCLIを「分離する為のシステム」なのに「埋め込みが生じてる」コード紹介になってて、これはこれでおかしいんですよ(笑)。つまり、恐らくOSS系のGUIアプリのモデルとしてはそんなに規範的ではない、と考えられるのです。つまり、誰も(OSSで恐らく想定している)正しいGUIアプリの書き方を知らない。</p><br /><p>いや、考えてみれば酷い状況でしょ(笑)?そうなんですよ。LinuxなんかのGUIアプリのモデルってのは恐らく基本的な設計思想とは別な方向にひん曲がって行ってる気がします。それもこれも提供されているGUI Builderの「やり方」があまりにもお粗末だ、ってのが絶対一要因に違いない。メチャクチャですね。少なくとも完全なRAD(Rapid Application Development)が提供されてたら、もっとLinuxのGUIアプリはWindowsに比べて良く整理されていて(つまりメインテナンスがラクで)、もっと発展してたかもしれません。基本的な設計思想に逆らうからこう言うハメになってるんじゃないかな、って思います。RADなGUI Builderはむしろ思想を実現する肝なんですよ。</p><br /><p>とまあ、自分なりの現状分析、っつーか、要するに愚痴なんですけど(笑)。まあ、少なくとも現状に於いてはGUIのコードを書くにもCLIでのコマンドの知識が必要だ、って事で。取り敢えずは取っ付きやすそうなwxPythonを調べていたわけです。</p><br /><p>ってなワケで、またもや極私的な備忘録。書いとかないと忘れちゃいそうですからね。上で「GUIとCLI部分の分離が本来のスタイル」って言いながらそこまでまだ出来てないんですが、それは今後の課題と言う事で。取り敢えずGUIアプリをwxPythonを使って「一本仕上げる」事にします。はい。</p><br /><p>お題は単純な単語当てゲームです。<a href="http://ja.wikipedia.org/wiki/%E3%83%8F%E3%83%B3%E3%82%B0%E3%83%9E%E3%83%B3_(%E3%82%B2%E3%83%BC%E3%83%A0)">ハングマン</a>と呼ばれるヤツですね。このコードには元ネタがあって、ピアソンエデュケーションから出版されている<a href="http://www.pej-hed.jp/washo/263.html">Pythonで学ぶプログラム作法</a>を元にしています。原著はWebサイト<a href="http://www.freenetpages.co.uk/hp/alan.gauld/">Lerning to Program</a>なんですが、本と違って改訂されて来て、現時点ではWeb上で読めるハングマンのコードはありません。残念ですが。</p><br /><p>元々書籍の方ではPythonとTcl/tkのインターフェースである組み込みのTkinterモジュールを使ってGUIで書かれていました。それをwxPythonを使って書きなおしてみれば、wxPythonに慣れるだろう、と言うのが目論見です。また、この本では不完全ながらもGUIとCLIのコードを分けていて、言い換えるとやり方によっては基本ロジック(つまりCLI側のコード)を変更する事無く、GUIのフロントエンドを取り替える事が可能だ、と言う事を示唆しています。</p><br /><p>でははじめますか。</p><br /><p>あ、その前に材料用意しないといけませんね。必要な材料は次の二つです。</p><br /><ol><br /><li>英語の辞書データ</li><br /><li>ハングマンの画像7枚(それぞれhm0、hm1、hm2・・・と名付ける。画像形式はお好きなように。サイズは250 x 180〜190辺りが適切。)</li><br /></ol><br /><p>辞書データはいいでしょう。英単語当てなんで、まずは英単語辞書が無いとお話になりません。これを別ファイルに用意しておいて、そこからランダムに問題となる単語を選び出すスタイルとします。ご自分で作成してもいいですし、Web上探せばテキトーに使えるモノが引っかかるかもしれません。あるいはPCに詳しい人なら、例えばワープロの辞書なんかから情報抜き出したりも可能でしょう。それで取り敢えず作業フォルダ内に「hangman.words」とでも名づけて保存しておく。</p><br /><p>2番の方が厄介ですね。ハングマンと言うゲームは(少なくともここでは)プレイヤーは6回まで間違える事が出来ます。ハングマンと言うのは「絞首刑に合った男」と言う、まあ、如何にもアメリカ的なちょっと笑えないユーモアがあるゲームなんですが(笑)、要するに、単語に何のアルファベットが使われているのか。その予想が当たってたらいいんですけど、予想が外れていた場合、少しづつ「絞首刑に合った男」の画像が明らかになっていきます(通常は単純な線画で、要するに線が描き加えられて行く)。6回間違えると「絞首刑に合った男」の線画が完成し、そしてそこでゲームオーバーとなるわけです。</p><br /><p><img src="http://media.tumblr.com/tumblr_lljfnmCWWe1qb0ini.png" /></p><br /><p>まあ、そういう絵を用意しとかなきゃなりません。ペインタやGimpでご自分で作成しても良いでしょう。僕は画像作成がメンド臭かったんで、もっとアクロバットなやり方で画像を用意しました(笑)。</p><br /><p>ところで、<a href="http://www.pej-hed.jp/washo/263.html">Pythonで学ぶプログラム作法</a>の著者であるAlan Gauld氏は割にオブジェクト指向大好きっ子みたいで(笑)、書籍は23章から成り立ってるんですが、オブジェクト指向が登場する第17章以降、これでもか、と言う程オブジェクト指向コードが蔓延していきます(笑)。そして、その抽象化の方法論に対して一家言持っている模様です。曰く、</p><br /><blockquote><em>このプログラムは、最初からオブジェクトを使って構築することにする。作成するプログラムに必要なクラスの計画を立てるときには、まず、プログラムが実際に行う処理について可能な限り抽象的に考えなければならない。今作成しようとしているのは、コンピュータが生成した何らかの「正解」(target)を当てることを目的として、いくつかの推測(guess)が提示される「ゲーム」(game)である。</em></blockquote><br /><p>まあなんともはや(笑)。ハングマン「如き」のゲームで抽象化、ってのもアレなんですけどね(笑)。まあ、ここではそう言う方針なんで、しょーがないでしょう(笑)。要するに問題の単語を出して、入力を受け取って、判定する、と言う「抽象機構」をここでは最初に作っています。そしてそれが<strong>フレームワーク</strong>だと力説します(笑)。まあ、そうですね、フレームワークってばフレームワークでしょう。極めて限定的で小さい抽象クラスのカタマリですが、プログラミング初心者にOOPのパワーを見せるにはいい方針かもしれません。そうか(笑)?</p><br /><p>まあ、ここではちょっと端折りたいので、取り敢えずコードを紹介しましょう。</p><br /><script src="https://gist.github.com/987925.js?file=game.py"></script><br /><p>これをgame.pyとします。</p><br /><p>さあて、これ解説必要ですかね(笑)。まあ、単純ってば単純なんですが単純過ぎて・・・(笑)。抽象化に成功してる、って事なんでしょうけれども(笑)。returnばっかやん、とも見える(笑)。あんま単純過ぎても動作が全く想像出来ないっつーか・・・(笑)。</p><br /><p>余談なんですが、こないだTwitterで@kaorin_linux氏と面白い話してたんですよ。曰く「ガンダムをOOPでプログラムする場合どうすっか?」みたいな議題でね。果たしてガンダムをプログラムする場合、ルートのスーパークラスを作成して「人型ロボット」と言うクラスを作るべきか否か。OOPの論法から言うと「作るべき」なんですよね。理論的には。</p><br /><p>現場主義の@kaorin_linux氏は「必要ない」と言う立場でした。その代わり、Zガンダムとか∀ガンダムとか作る場合は、ガンダムと言う性質を継承して作ればいいじゃないか、と。その通りですよね。現場主義の@kaorin_linux氏的な意見です。</p><br /><p>一方、OOPの理論ってのは突き詰めていくとある種「机上の空論」でもあるんですよ。では本当に「人型ロボット」がルートたるべきか、っつーと分かんない。別にロボットに限定する必要もなくって、その上に「人型の何か」と言うもっと抽象的なスーパークラスも考えうる。そうしたら「人型宇宙人」とか「人型へげもげ」とか色んなブツが継承出来る。そして本当は「人型」に限定する必要があるのか、とか。もっと上に「何か」があっても良いのでは・・・・・・突き詰めるとキリがありません(笑)。</p><br /><p>意外と、OOPのルートクラス設計的な思想ってのは、それこそJavaのように「豊富なライブラリを提供する前提の」言語とその言語設計者に一番有利なモノかもしれません。彼らがその「言語内のあらゆる事を」決められる立場ですから。Common LispのOOPでもTと言うクラスを頂点にヒエラルキーが構成されています。「見事だ!」なんじゃなくって、最初からそうデザイン出来る人はそう決められる、と言う「だけの」話だと思います。僕ら一般ユーザーはそんなヒエラルキーを考えろ、っつっても、所詮他人の褌で相撲を取るようなもので、初めから限界があるような気がします。</p><br /><p>と言うわけで、上のコードも「所与のライブラリ」と考えて単純に使った方が良いのかもしれません。</p><br /><p>と言うわけでgame.pyのコード解読は取り敢えず<strike>メンド臭いので</strike>止めて、CLI版のハングマンのコードを見てみましょうか。これがGUIでのハングマンの「本体」、hangman.pyです。</p><br /><script src="https://gist.github.com/987925.js?file=hangman.py"></script><br /><p>これどうなんでしょうね〜(笑)。結局Gameクラスとか継承しててもかなりの部分がオーバーライドされてるように見えませんか(笑)?って事は実際は、<strong>殆どスクラッチから書き始めるのと変わらない</strong>ような気がするんですが(笑)。</p><br /><p>まあ、こう言うの見ると@kaorin_linux氏が言ってる事の方が現実的には正しいような気がしますね。<strong>作らなきゃいけない部分から取り敢えず手をつけてみる</strong>と。もちろんライブラリ的なモノを作る必要性も出てくる事もあるでしょうけど、少なくとも個人的にはこの例はあまり上手く無い例のような気がします(だから<a href="http://www.freenetpages.co.uk/hp/alan.gauld/">本家サイト</a>からネタが消えたのか・笑)。</p><br /><p>さて、本から丸写ししてコメント記述してるんで、これも特に解説は要らないでしょう(こればっか・笑)。ただ、これをPythonインタプリタで走らせてみて、全く画像が無いけど、単語当てゲームとしては機能しているのを確認してください。つまり、ゲームとしてのロジックはほぼ実装し終わっている、と言う事です。これがCLIでのハングマンですが、このファイルに直接手を入れずに次の段階でGUIのゲームへと変身させます。</p><br /><h3>多重継承は悪か?</h3><br /><p>さて、いよいよ本題です。CLIのプログラムにGUIフロントエンドを引っ付ける。ここでは<a href="http://www.pej-hed.jp/washo/263.html">Pythonで学ぶプログラム作法</a>の流儀に従って、GUIフロントエンドは中核機構となるwx.FrameとCLIゲームであるhangmanモジュール内のHangmanクラスを<strong>多重継承</strong>します。</p><br /><p>多重継承って結構嫌われていますよね。当然で、平たく言うと、ヘタな継承の仕方によっては有向循環グラフになる危険がある。グルグルグルグルグールグル、と言うアレです。孫クラスのつもりだったのに曽祖父クラスになってしまっていた。自分で自分を継承してたりして・・・まあ、怖い話ですわな。だからインターフェースだMix-inだ、と言う代替手段が提供されてきたのでしょう。</p><br /><p>一方、Pythonは「プログラマの良心に任せて」多重継承機能を提供しています。そしてそれが意味する事は「使いすぎは良くない」でも「使いどころさえ間違えなければ」超強力だ、と言う事です。</p><br /><p>そして、CLIプログラムをGUIと統合する「その瞬間」こそが、その使いどきでしょう。全く異質(に見える)のアイディアであるCLIとGUIを自然に貼り付ける「接着剤」の役目としては多重継承がまさしく適していると思われます。</p><br /><h3>wxPythonの直接手書きは諦める(爆)</h3><br /><p>ところで、最初は、手書きでwxPythonのコードをゴリゴリゴリゴリ書いてくつもりだったんですよ(笑)。せっかく勉強したんだし。ところが、途中でGive Upしました(爆)。いくつか理由があります。</p><br /><p>まずはTcl/tkバインダのTkinterのコードって手書きでも相当短く出来るんですよ。僕はTcl/tk自体は全然分からないんですけど、どうもシンプルな記述で意図的な配置に出来るらしい。正確に言うと、wxWidgetsに比べるともっとバッチ処理に近く、基本的にコードを並べた順に順序良くウィジェットを配置していけるらしい。もちろん、wxWidgetsも基本的にはそう言う部分があるんですが、配置に関して言うと強力なようで同時に弱点も内包してる気がします。平たく言うと配置に使うSizerですね(笑)。こいつは配置をラクにするように設計されてる筈なんですが、全体の画面構成を綺麗にする為にSizerを複数重ねて配置していく必要が出てくるんです。wxGladeなんかのGUI Buiderならこれはラクなんですが、マジでCLIで書くとアタマが痛くなっちゃうんです(笑)。CLI泣かせの機能ですね(笑)。</p><br /><p>第二に習慣の問題があります。これはTkinterで書かれたGUIを如何にwxPythonに移植するか、と言う挑戦でもあるんですが、習慣的にTkinterはimport時に</p><br /><script src="https://gist.github.com/987925.js?file=bad_import.py"></script><br /><p>ってやっちゃうんですよ(笑)。これは実は現在のPythonでのライブラリインポートでは薦められた方法じゃない。要するにライブラリ名を(ドット表記での)接頭語にしないようにする「オマジナイ」なんですが、今の流儀では殆ど</p><br /><script src="https://gist.github.com/987925.js?file=bad_import.py"></script><br /><p>と言うようにして、明示的にライブラリ名を接頭語として用い、「ライブラリ同士で名前がぶつかったりしないように」してると思います。ただ、Tkinterの場合は歴史が古いせいか、こう言う書き方が跋扈してるんですね(笑)。</p><br /><p>まあ、少なくとも(何故か)<a href="http://www.pej-hed.jp/washo/263.html">Pythonで学ぶプログラム作法</a>では前者の書き方が用いられています。これは移植の時に困るんですよ(笑)。</p><br /><p>何が困るか。要するに「見慣れないメソッドが」登場したとき、接頭語が無いお陰で果たしてTkinter内のメソッドなのかどうなのか確認が取りづらい、と言う事です。そうなるとそれを調べながらCLIでコマンド書いてく、ってのは至難の業なんですよね。少なくとも時間がかかりすぎる。</p><br /><p>つまり、先にwxGladeとか使って画面が立ち上がるようにしてて、生成されたPythonコードと比較しつつ、対応取って行って調べた方が早いんです。そう言う理由もあります。</p><br /><p>最後に、「何がしたいか」で調べるのが難しい。要するに逆引きですよね。wxPythonも例に漏れませんが、ある「既知の」クラスやメソッドに付いては、ある程度調べる事が可能です。逆に「これってどーやるの?」ってなった時、CLIで書いてる場合「アタリを付けて」調べるのが難しい。だって名前をそもそも知らないんですから。</p><br /><p>例えば、今回の準備段階として、wxPythonのチュートリアルを2つ程こなしました。ただ両者とも「画像表示に対しては全く触れていない」。つまり、どう言うクラスのどう言うメソッドを使えば良いのかサッパリなんです。今回使用したのはwx.StaticBitmapと言うクラスですが、こんな名前かどうか思いつくのは至難の業です。結局、wxGladeでコードを吐き出させてクラス名を調べてからリファレンスをあたった方が効率的だったんです。</p><br /><p>そんなわけで、一旦wxGladeで思ったデザインを作って、そいつをPythonで吐かせて、そのコードを調べながらGUIを作り上げていく、と言う方針を取りました。っつーか取らざるを得なかったのです。</p><br /><h3>画面イメージ</h3><br /><p>ボタン配置等の画面イメージは以下のようなカンジです。</p><br /><p><img src="http://media.tumblr.com/tumblr_lljfoyaQSi1qb0ini.png" /></p><br /><h3>GUI版ハングマンのコード</h3><br /><p>まずは作成したGUI版ハングマンのコードから。</p><br /><script src="https://gist.github.com/987925.js?file=wxHangman.py"></script><br /><p>結構wxGladeが吐き出したコードに手を入れています。本当はダメみたいなんですがね(笑)。同時に吐き出すxmlが拗ねちゃう模様で(笑)。</p><br /><p>まず、この部分は簡単ですね。</p><br /><script src="https://gist.github.com/987925.js?file=keys.py"></script><br /><p>これはお察しの通り、GUI版ハングマンのボタンの配置と対応しています。こいつをクリックして、推測した使われてる文字をGUI版ハングマンに伝えます。ただ、これはボタンそのものではなく、一種テンプレートです。二重のリストになっています。</p><br /><p>次はGuessクラスを継承したhmGUIGuessクラスです。</p><br /><script src="https://gist.github.com/987925.js?file=hmGUIGuess.py"></script><br /><p>ここも特に解説は要らないでしょう。</p><br /><p>次からGUI版ハングマンの心臓部、hmGUIクラスです。こいつがwx.Frameとhangman.Handgmanを多重継承します。まずはここから。</p><br /><script src="https://gist.github.com/987925.js?file=hmGUIinit.py"></script><br /><p>まずは初期化メソッド__init__。ここで分かる通り、二つのクラスを継承しているので両クラスに基づいて初期化を行ってるのが第一点。</p><br /><p>imgpathってのは本体フォルダ内の画像フォルダの位置ですね。こいつを使って、相対パスから計算して初期画像を表示する準備をします。ちなみに画像はhm6(この場合は.jpg)から辿ってhm5->hm4->...->hm0と進んで行きます。初期画像は6番ですね。その方が入力指定したアルファベットが間違えた場合の「残りの回答出来るチャンスの数」と対応させやすい。</p><br /><p>lettersってのは実は上で見たアルファベットのボタン本体の事です。辞書型を使ってますが、初期状態だとボタンもヘッタクレもないです(笑)。こいつに後でボタンを作成した状態をぶち込んで行きます(笑)。何故辞書型なのか、はその時に。</p><br /><p>そして、タイトルバー表示等の調整をする__set_properties()を呼び出し、displayStart()メソッドを呼び出して初期化は終了です。</p><br /><p>__set_properties()はwxGladeによって作成されたモノです。手書きでコード書く場合はあんま必要ないです。displayStart()はサイザーが乗ったメソッドです。こっちに関しては後述します。</p><br /><p>次は表示に関して扱うdisplay()メソッドです。こいつはCLIのdisplay()をオーバーライドした状態になってます。っつーか事実上殆ど書き換えられてますね。</p><br /><blockquote>実はこの辺がOOPの無駄な部分なような気がするのだが・・・。「ちょっとだけ変更する」なら有効だろうが「大幅に変更する」のならあまり意味があるとも思えない。結局コード記述量が増えるわ無駄になるわ、と言う気がしてる。</blockquote><br /><script src="https://gist.github.com/987925.js?file=display.py"></script><br /><p>「表示用メソッド」なんで表示に関する動作が詰め込まれています。まず冒頭で、wx.StaticText(一般にラベルと言われる部分)に何を表示させるか、それが記述されていますね。これはこのメソッドの後半でif〜else文で切り替えられるネタ元です。この辺は難しくないでしょう。</p><br /><p>まずは第一のポイントですが、</p><br /><script src="https://gist.github.com/987925.js?file=letters.py"></script><br /><p>ここ。このlettersってのはさっき初期化したlettersと同じものです。ただし、このdisplay()メソッド内では「既にボタンとして成り立っている」前提になっています(笑)。あら、いつの間に(笑)。まだボタン化するコード書いてないんですがね(笑)。まあ、そう言う前提として見てください。</p><br /><p>ここのポイントはですね。ハングマン、と言うゲームに於いては、もう一度繰り返しますが、推測したアルファベットを与えるわけですよ。んで、それが当たったにせよ、外れたにせよ、その文字は「二度と入力しない」んです。例えば'E'と示してみて、それが正しかったにせよ間違ってたにせよ、二度目の'E'の入力はあり得ない。</p><br /><p>と言う事は。ゲームデザインとしては、「同じ文字の二度目の入力があり得ない」って事は、単純に考えると「一度押されたボタンは二度と押せないようにする」のがセオリーですよね。つまり、ここでのlettersってのは実はwx.Buttonと言うクラスのインスタンスになるわけで、Disable()と言うのは「ボタンを押せないようにする」メソッドなんです。これがまた、wxPythonの公式ページにも記述が無い程の(笑)便利機能なんですが(笑)。どないなっとんねん(笑)。</p><br /><p>もう一つのポイントは次の二行です。</p><br /><script src="https://gist.github.com/987931.js?file=image_renew.py"></script><br /><p>thefileと言うのは画像(hm*)ですね。ここでoutcome(ゲームの残り回答数)と適合したパスをpythonの相対パスを利用しつつ生成してます。ここはもう良いでしょう。あと5回答えられるならhm5が選ばれるし、あと1回しかなかったらhm1が選ばれる。</p><br /><p>次はtheImgです。これも__init__で画像の初期化により初期パスは指定してるんですが、一方画像オブジェクトはいまだ作られてません(笑)。だってコード書いてないもの(笑)。それでもいきなり登場して良いのがOOPです(笑)。self付いた変数ってクラス内大域変数だからあっちゃこっちゃ飛んでてヤダ、ってぇの(笑)。これだから困るんだってばよ(笑)。</p><br /><p>与太はさておき、こいつはwx.StaticBitmap()というクラスのインスタンスです。要するにこの時点で(まだコード書いてないけど!)画像オブジェクトだ、と言う事です。</p><br /><p>ちょっと余談めいた注釈になりますが、例えばこのGUIハングマンで用いるクラスではwx.StaticBitmap()とかwx.StaticText()とか、良くStatic、と言う形容詞が付いています。他の人がどーだか知らないんですが、Staticって聞くと、僕なんかは「静的な」「変更出来ない」「固定された」って言うような意味に捉えちゃうんですよね。つまり一回表示されたら変更不可みたいな。</p><br /><p>だからコンピュータ用語って困るんですよ(笑)。簡単にStaticだ、とかDynamicだ、とか言いますが、実際問題コンテクストによって変わる。そしてコンテクストを決定するのは形容詞の筈なんで、自ずとから矛盾してますよね(笑)。何じゃそりゃ、と。</p><br /><p>ですから、僕みたいに直感的に誤解しちゃう人の為に一言。ここで言うStaticとは「ユーザー入力を受け付けない」って事です。つまりプログラム内部から変更する分には全くOKって意味なんです。ああ、紛らわしい。</p><br /><p>今、ゲームの進み具合(つまり、あと答えられるのは何回なのか)によって表示すべき画像が変わります。画像オブジェクト、平たく言うとインスタンス化したwx.StaticBitmap()の画像パスを変更するメソッドが<a href="http://www.wxpython.org/docs/api/wx.StaticBitmap-class.html">SetBitmap</a>なんです。</p><br /><p>また良く知らないんですが(笑)、多分いわゆるjpgとかpngとかはそのままじゃビットマップ画像として認識されないのでしょうか。多分変換が必要で、その為の変換クラスが<a href="http://wxwindowsjp.sourceforge.jp/html/2.8.4/wx_wxbitmap.html">wx.Bitmap</a>なのでしょう。ホント、よう知らんけど(笑)。</p><br /><p>wx.Bitmapの第一引数はファイル、第二引数は第一引数で与えられたファイルタイプです。つまり、例えばjpgなら本当はwx.BITMAP_TYPE_JPEGを与えるべきなんでしょうが、メンド臭いんで(笑)、何でもオーケーのwx.BITMAP_TYPE_ANYが与えられているのです。</p><br /><script src="https://gist.github.com/987931.js?file=tiny_methods.py"></script><br /><p>ここは小粒ですね〜。getTarget()はまあいいですよね。Quit()は、終了ボタンが押されたら反応するメソッドでウィンドウを閉じます。要するにゲームを終了させるメソッドです。</p><br /><p>reset()もリセットボタンを押した時にゲームを初期状態に更新するメソッドです。ただ若干複雑なんでコメントが付いててなお重要なところを。まずは</p><br /><script src="https://gist.github.com/987931.js?file=enableButtons.py"></script><br /><p>これは先ほどのdisplay()で見た、「ボタンを使用不可にする」事と逆の事を行っています。ゲームに再度挑戦する際に、ボタンが使えないとどうにもこうにもブルドッグ、だからです(謎)。相変わらずlettersはアルファベットのボタンを表しています。つまり、wx.Buttonのインスタンスに於いて、メソッドEnable()とDisable()は真逆の関係だと言う事です。</p><br /><p>次はここですね。</p><br /><script src="https://gist.github.com/987931.js?file=renewImgAndCond.py"></script><br /><p>一行目はさっきやったのと殆ど同じです。画像を書き換えてる(っつーかより正確に言うと参照すべきパスを変えている)。ここでは__init__()で定義されているself.firstImgを「大域変数よろしく」引っ張ってきています。</p><br /><p>二行目は表示されるべきモノですね。実際、self.getResult()で文字情報が入ってる筈なんですが、表示されるのはそれに対応したアンダーバーだ、と言う事になります。</p><br /><p>そして三行目。このstatusも実はインスタンスです。まだ定義されてません。クラスはwx.StaticText。これもSetLabel()と言うメソッドで表示を変えます。wx.StaticBitmapのSetBitmap()メソッドの文字版ですね。</p><br /><p>__set_properties()はいいでしょう。wxGladeが自動で作ったメソッドですし、ここではWindow上部のタイトルバーにアプリケーション名を表示しているだけ、ですから。</p><br /><p>さて、いよいよ最後のメソッド、displayStart()です。</p><br /><script src="https://gist.github.com/987931.js?file=displayStart.py"></script><br /><p>まあ、大体ここまで読んできてくれた人だったら何やってるかおおまかなトコは掴めるでしょう。theImgと言う画像オブジェクトもここで一行目で設定されていますし、同じくラベルオブジェクトであるstatusもここで作成されています。</p><br /><p>が、やっぱ見て分かると思いますが、メンド臭いのがSizerですね。全部で5種類程使っています。これ、自分でコード直接書いてたらこんがらがりますよ(爆)。よってSizerに関して言うと、あんま細かい事いいません。ただ、最後のsizer_*.Addの第二引数でSizerの比率を変えたりしています。デフォルトでは一対一に分割、って状態になってますが、それを実際画面見ながらパラメータ微調整する、ってカンジですか。この辺はプログラミングじゃあないですよね、正直なトコ。</p><br /><p>そしてsizer_*.Addの第一引数にはそのSizerの上に「何が乗ってるか」を表しています。ボタンが乗ってる場合もあるし、Sizerの上にSizerが乗ってる場合もある。はあ、画面デザインって大変です(爆)。</p><br /><p>さて、ここで一番重要な部分は次の部分です。ここで'A'〜'Z'のボタンを生成しています。</p><br /><script src="https://gist.github.com/987931.js?file=makeBoard.py"></script><br /><p>実はここがこのプログラム最大の山場で、かつボタンとシグナルを結びつける全テクニックが突っ込まれています。また、プログラミングに対する大変面白い考え方が伺えます。もっとも僕が考えたんじゃないんですけどね(笑)。</p><br /><p>実はwxGladeに'A'〜'Z'のボタン生成のコードを吐かせるのも当然可能なんですけれども、それやっちゃうと27個のボタンがソースコード上並びまくる、と言う大変な状態に陥ります。かつ、Bind(あとで後述)がメンド臭くなる。しかもそれをSizer上に置くトコまで指定せねばならない。上のコードはそれをたった9行で行っちゃうスグレモノです。およそコード量が1/3、いや1/6、いや1/9に減った、と言えるでしょう。</p><br /><p>まずは冒頭のkeysを使ってrowを、そしてrowの中からchを取り出す二重ループ構造になっていますね。そしてchが空の文字列だった場合、</p><br /><script src="https://gist.github.com/987931.js?file=lettersAndGridSizer.py"></script><br /><p>が実行される。ここで代入されてるgrid_sizer.Addですけど、これはwxGlade上ではSpacerと呼ばれています。隙間を埋める部品、って事なんですけど、実はコード上ではそう言った名前は使われていません。いずれにせよ、""に対応する場所には何も置くな、と言う指示になっているのです(あるいは、30 x 30の小さな「何か」を置け、って事でしょうか)。</p><br /><p>では空文字列じゃなかった場合です。まあ、取り敢えず最初のactionは置いておきますか。その次のコードでAlan Gauld氏の手腕が光る。珠玉のコードがたった一行で示されます。</p><br /><script src="https://gist.github.com/987931.js?file=pearlInOneLine.py"></script><br /><p>正直な話、最初これ見た時何やってるんだかサッパリだったんですよ。しかし考えてみると非常に良く出来てる。これはこう言う事なんです。最初に__init__でlettersを辞書型にしておいたのがここで効いてくる。</p><br /><p>つまりchがlettersと言う辞書型のキーになるんですね。そしてその「値」この場合オブジェクトwxButton()ってのがここで結び付けられるわけです。要するにハッシュであるlettersに適切なキーを渡すと必ず返り値でユニークなボタンオブジェクトが返って来る。ね?メチャクチャクールな方法でしょう(笑)。こりゃすげえや、って思いました。</p><br /><p>しかし、ボタンを生成するだけじゃダメです。イベントとして動作と結び付けないといけません。ここで結びつける対象は今後回しにしたactionなんですが、これらを結びつけるメソッドがBindと言います。そして第一引数にはボタン用のイベントだよ、と知らせるwx.EVT_BUTTON、第二引数には結びつける対象の関数(この場合はaction)、そして第三引数にはそのボタン自体が入ります。</p><br /><script src="https://gist.github.com/987931.js?file=bindButton.py"></script><br /><p>これはボタン用のBindの書式です。ですが、例えばプルダウンメニューなんかでも基本変わりません。第一引数がwx.EVT_MENUになったりするだけです(色々あるんで検索するのが大変でしょうが・笑)。第三引数もいいですね。ハッシュ(辞書型)にキーchを与えて返って来るwx.Buttonインスタンスとactionが結びつく。そしてそのactionは最初にラムダ式として定義されてるのです。</p><br /><script src="https://gist.github.com/987931.js?file=actionIsLambda.py"></script><br /><p>この場合、ラムダ式は第一引数にイベントを受け取り、第二引数にchを受け取り、第三引数がself、と言う、ちょっとPythonとしては珍しい記述に落ち着いています(っつーか引数の順番として、はね)。そして、先ほど定義した表示用メソッドdisplay()にchを渡す、と言う作業が閉じ込められています。これで各アルファベットボタン毎に何が表示されるべきか、完全に定義されるわけです。かっこいー!!!!凄いアイディアですね。</p><br /><p>そして、最後にボタンをgrid_sizer上に行儀良く並べていけば良いわけです。</p><br /><script src="https://gist.github.com/987938.js?file=buttonOnGrizSizer.py"></script><br /><p>たった9行で27個のボタン生成、27個のボタンとメソッドを結びつけ、配置までやってのける、ってなあ凄すぎます。でも良く考えてみると、これって凄いだけじゃあないんです。実はあるアイディアが背後に見え隠れしています。それは一体何でしょう?</p><br /><h3>Lisp的考え方</h3><br /><p>僕も最初にお題を見た時、「27個もボタンしこしこ書きたくねえな。何とか自動生成できねえかな」って思いました。ところが問題なのは、代入するクラスの方じゃなくって、代入される変数名の方だったんです。こいつらリテラル、と呼ばれるブツは通常自動生成は出来ません。たった一つの言語を除いて。</p><br /><p>そうですね。Lispです。Lisperだったらこう言うお題を見た場合、即刻考えるのはまずは「変数名の自動生成」でしょう。そして恐らくマクロにしちゃいます。ところがこれがPythonじゃ出来ない。こりゃ困ったな、って思いました。</p><br /><p>しかしこれをAlan Gauld氏は「辞書型」と言うデータ型を用いて華麗に切り抜けました。確かに凄い見事なんです。ただ、この手法は実はLisp的発想なのかもな、と気づいた。Alan Gauld氏が狙ったのは、実はPython上でのシンボル型のエミュレーションだったのでは、と。それを辞書型用いて実装してみた、ってのが本当のところではないか、と。</p><br /><p>まあ、この辺は余談なんで、Lisp知らない人にはあんま関係ないです。がLispってのは全般的に(Java的な意味ではなく)データ型で組み立てられていて、いわゆるフツーの言語で言うリテラルさえ持ってません。「地の文」を構成する「何か」が無いわけです。フツーの言語的感覚で言うとね。その代わり、式を組みたてる「字」っつーのかな、それらは特に「シンボル型」と呼びます。れっきとしたデータ型なんです。</p><br /><p>さて、Lispではインタプリタに何か入力すると、そのシンボルは即刻シンボルテーブルと言う内部の一種データベースに登録されるわけです。これを<strong>シンボルをインターンする</strong>とかLisperは言うわけなんですけど、実はこのPythonコードのこの部分は、この「シンボルのインターン」を明示的な方法を持ってPythonで実装したんじゃないのかな、と思えるわけです。すんごくシステム的には良く似てるんですよ。</p><br /><p>多分PythonでLispを実装する、的なネタですと、このシンボルのインターンのシステムと言うのは、Lisp-1やLisp-2に限らず、恐らく辞書型で実装するのが一番効率が良いです。ハッシュは速いですし、シンボルとしてのキーを検索するには向いている。また、キーをメソッドっつーか関数としての「値」に結び付ける発想も至極良く似ています。このシステムは、かなりSchemeなんかに近いんですね。このコードの意味が分かった時、正直「一本取られた!」って思いました。非常にLisp的な実装ですし、明らかにLispの一部(っつーか考え方)を導入しています。</p><br /><p>もちろんAlan Gauld氏はLispも知っている。初心者プログラマがPythonを終えた後に学ぶ言語としてLispを挙げている程です。そしてこう言う風にLispのプログラミングテクニックと言うより、その「発想/構造」を持ち込めるようになる、ってのが、ひょっとしたらエリックレイモンドが言っていた「悟り」の部分なのかもしれません。</p><br /><h3>終わりに</h3><br /><p>この見事なテクニック見たあとでは何も言えませんね。基本的にボタンとイベントを結びつける基本テクニックは上のコードで見たまんまですし、あとは地道に読めば分かる範囲です。取り敢えずCLIのコードからスタートして「多重継承」を経由して、GUIのアプリケーションへ変換する事は出来ました。wxPythonを使った「アプリケーション」を一本作り終えたわけです(よね?)。</p><br /><p>[リセット][終了]ボタンに関して言うと、例えばGTK+をそのまま使うGladeなんかはGTK+が用意してくれているボタン画像なんかを使えたんですけど、wxGladeはどうだったかな。何か見つからなかった(笑)。その辺今後の課題かもしれませんね。まあ、課題って程でもないですけど(笑)。</p><br /><p>最後に。一応このハングマンと言うサンプルは<a href="https://github.com/cametan001/Hangman">GitHub</a>に上げてあります。</p>cametan_001http://www.blogger.com/profile/04882440058326617382noreply@blogger.com0tag:blogger.com,1999:blog-4018043311243103708.post-11415201704170253092011-05-24T08:56:00.004+09:002011-05-24T09:13:29.961+09:00プログラムのデザイン<p>以降の章では多くの段階をを要するプログラムの開発法を示します。僕らはプログラムとして何が必要か、何を無視すれば良いのか決めなくてはなりません。プログラムが何を用い、何を作り、どのように入出力が関連してるのか、知る必要があると言うことです。僕らが作っているプログラムに於いて、果たしてSchemeがデータを操作する基本機能を提供しているのか否か、も知らないといけません。もしそうじゃないのなら、それら補助機能を実装しないとならないでしょう。プログラムを作り上げた暁には、意図した動作をするかどうかもチェックしなければならないのです。それによって、シンタックスエラーや、実行時間の問題、あるいはロジックの間違いが明らかになるでしょう。</p><br /><p>これらカオスを順序良く整理する為に、<strong>デザインレシピ</strong>と言う方法を導入します。デザインレシピとは開発段階を示した規定の事で、要するに僕らがやらなければならない事を順序良く示したものです。今まで行なってきた事を見ても分かるでしょうが、プログラムを作る、と言う事は、最低でも、以下の4段階が必要になってきます。</p><br /><script src="https://gist.github.com/987909.js?file=sample.ss"></script><br /><h3>プログラムの目的を理解する</h3><br /><p>プログラムのデザインのゴールは、データを加工し、加工済みのデータ返す機構を作り上げる事です。と言うことは、どんなプログラムの開発に於いても、どんな種類のデータを入れて、どんな種類のデータを出すのか、それらに対して明示的な名前を付ける事から始めないといけません。僕らはそれを<code>CONTRACT</code>(契約)と呼んでいます。</p><br /><p>一番最初に書いたプログラムのうちの一つ、<code>area-of-ring</code>に対してのcontractは次のようなものです。</p><br /><script src="https://gist.github.com/987909.js?file=sample1.ss"></script><br /><p>セミコロンは<code>COMMENT</code>を表しています。contractは二つの部分から成り立っています。左側の最初の部分はプログラムの名前です。右側にある二番目の部分は、どう言ったデータを用い、どう言うデータを返すのか、を表しています。そして、入力と出力は矢印で区切られています。</p><br /><p>contractを作成した後は<code>HEARDER</code>(ヘッダ)を作りましょう。そうすれば、プログラムの名前と入力の固有の名前を再確認する事が出来ます。それらの名前は(アルファベットでの)変数になり、プログラムの<code>PARAMETERS</code>(引数)として参照されます。</p><br /><blockquote>訳注:ちょっとこの辺、ヘッダと言う言い方は一般的ではない。ここで示されてるヘッダと言うのは、本体を持たない関数の形式的テンプレートを意図しているらしい。例えば、以下で見る<br /><script src="https://gist.github.com/987909.js?file=sample2.ss"></script><br />を「ヘッダ」と呼んでいる。なお'...'は省略の意味を持っていなく、実際に'...'と書け、と言ってるのである。つまり、テストケースを最初に書くべきだ、と言うのがこの本(HtDP)の主張で、関数本体を記述するのは、ある程度のガイドラインを作成してから、と言う事である。</blockquote><br /><p>ちょっと<code>area-of-ring</code>のcontractとheaderを見てみましょうか。</p><br /><script src="https://gist.github.com/987909.js?file=sample3.ss"></script><br /><p>これは最初の入力が<code>outer</code>であり、次の入力が<code>inner</code>である事を示しています。</p><br /><p>最後に、contractと引数から、プログラムに対しての短い<code>PURPOSE STATEMENT</code>(目的)を作る事が出来ます。<code>PURPOSE STATEMENT</code>とは、プログラムが<em>何を</em>計算するのか、に付いて短く記述したものです。僕らがここで作るプログラムの殆どでは、一行か二行で充分でしょう。しかし、開発すべきプログラムが大きくなればなるほど、プログラムの目的に付いて詳細に記述する必要が出てきます。</p><br /><p>以下に最初の例をあげておきます。</p><br /><script src="https://gist.github.com/987909.js?file=sample4.ss"></script><br /><p><strong>ヒント:</strong>もしも、問題文が数式を含んでたとしたら、式の独立変数の数がプログラムが必要な入力の個数を教えてくれるでしょう。</p><br /><h3>プログラムの例</h3><br /><p>プログラムが何を計算すべきかより良く知るには、入力例を考え、その出力が何になるのかを予想することです。例えば、<code>area-of-ring</code>の場合、5と3と言う入力に対しては50.24と言う値を返す必要があります。と言うのも、外側の円の面積と内側の円の面積の差分が50.24だからです。</p><br /><p>この例をpurpose statementに加えてみましょう。</p><br /><script src="https://gist.github.com/987909.js?file=sample5.ss"></script><br /><p><strong>プログラム本体を書き始める前に</strong>例を作る事はいろんな意味で有用です。まず、テストに於いて、ロジックのエラーを見つける唯一の正しい方法だからです。もし、プログラムを書き上げてから例を考えたりすれば、僕らはプログラムの方を信用しやすい。と言うのも、プログラムを走らせた方が、プログラムがどう言う結果を返すか予測するより遥かに簡単だから、です。二つ目に、後に行うより複雑な例をやれば分かるでしょうが、例がある事によって、より計算過程に対して慎重になる事ができ、関数本体を書く際に批評的な態度を持つ事が出来ます。最後に、例はpurpose statementをより具体化してくれます。将来的にプログラムを解読する人たち、例えば先生であるとか、同僚だとか、はたまたプログラムの購入者等は、抽象的なコンセプトを具体化して書いてくれている事に感謝してくれるでしょう。</p><br /><h3>本体</h3><br /><p>最後にプログラム本体を書きます。これは<strong>"..."を全てを置き換える事を意味します</strong>。Schemeの基本機能や、あるいは僕らが既に<strong>定義済み</strong>かあるいは<strong>これから定義する</strong>機能を駆使して、引数から何を計算して答えとして返すのか全て表現していきます。</p><br /><p>与えられた入力に従って、どのような出力を返すのか僕らが理解さえしていれば、プログラム本体はそれらを定式化するだけで済みます。仮に、入出力の関係が数式で定義されているのなら、僕らはそれをSchemeに翻訳するだけで済む。一方、文字関連の問題が与えられたなら、慎重に式を作成しないといけません。最終過程まで、第二段階で作成した例に戻る事は助けになるでしょう。そこで特定の入力に対してどう出力を計算すれば良いのか確かめられますから。</p><br /><p>ここの例では、以前定義しましたが、明示されていない<code>area-of-disk</code>を使った計算を用いています。これがSchemeでの数式の翻訳になります。</p><br /><script src="https://gist.github.com/987909.js?file=sample6.ss"></script><br /><h3>テスト</h3><br /><p>プログラムの定義を完了したあとでも、僕らは今度はプログラムをテストしないといけません。最低でも、プログラムの例から見て、正しい出力が出てくるか確認するべきです。テストを簡略化する為に、<code>Difinitions</code>window(定義ウィンドウ)の一番下に、方程式のように例を書き加えておけばよいでしょう。その後、<code>Execute</code>button(実行ボタン)をクリックすればそれらは評価されて、プログラムが正しく動いてるかどうか確認出来ます。</p><br /><blockquote>訳注:この本(HtDP)は前提としてPLT Scheme(現PLT Racket)を用いる事になっている。従ってDifinitions WindowやExecute buttonと言うのはPLT特有の表現であって、一般的なSchemeの話ではない。要するにこう言う事である。</blockquote><br /><blockquote><img src="http://media.tumblr.com/tumblr_llf9wjrQim1qb0ini.png" /><br /></blockquote><br /><blockquote>PLT SchemeはIDE形式で提供されていて、上半分画面はDifinitions Window(定義ウィンドウ)と呼ばれている。Execute buttonは日本語ではそのまま実行ボタンとして表示されている。定義ウィンドウにSchemeコードを書いて、実行ボタンを押せば下のインタプリタに結果が出てくる。</blockquote><br /><p>テストでは、あらゆる入力に対して、正しい結果を返すかどうかは分かりません。と言うのも、通常、可能な入力と言うのは無限大にありうるからです。しかしながら、テストはシンタックスエラー、実行時間問題、ロジックのエラー等は明らかにしてくれるでしょう。</p><br /><p>誤った出力に関しては、僕らは特にプログラムの例を見直してみないとなりません。場合によっては例自体が間違ってる事もある。と言う事は、プログラムがロジックのエラーを含んでるか、あるいは例もプログラムも間違っているか。どちらにせよ、その場合は最初から開発をやり直さないとならないでしょう。</p><br /><ol><br /><li>Contact(契約)とPurpose(目的)とHeader(ヘッダ)作成 <br /><ul><br /><li>ゴール:関数の名前を付ける。入力データと出力データのクラス分け。目的を記述する。ヘッダを作成する。 <br /><ul><br /><li>やるべき事: <br /><ul><br /><li>プログラムに問題に適した名前を付けよう。</li><br /><li>関数が必要とする未知の事柄がいくつあるのか、そのヒントを求めて問題を研究しよう。</li><br /><li>一つの入力に対して一つの変数を取り上げよう。可能なら、問題で与えられ、明示された名前を使う事。</li><br /><li>選ばれた変数名を使って関数が何を作り出すのか説明しよう。</li><br /><li>contractを作成し、ヘッダを作ろう。<br /><script src="https://gist.github.com/987909.js?file=sample7.ss"></script><br /></li><br /></ul><br /></li><br /></ul><br /></li><br /></ul><br /></li><br /><li>例の作成 <br /><ul><br /><li>ゴール:例を通して入出力の関係を明らかにする。 <br /><ul><br /><li>やるべき事 <br /><ul><br /><li>問題から例に適切なものを探そう。</li><br /><li>良い例を心がけよう。</li><br /><li>可能なら、結果を良く確認しよう。</li><br /><li>例を作り上げよう。</li><br /></ul><br /></li><br /></ul><br /></li><br /></ul><br /></li><br /><li>本体作成 <br /><ul><br /><li>ゴール:関数を定義する <br /><ul><br /><li>やるべき事 <br /><ul><br /><li>どのように関数が結果を計算するか定式化しよう。</li><br /><li>Schemeの基本関数やその他の関数と変数を駆使してSchemeの式を開発しよう。</li><br /><li>可能な場合には、問題の数式表記を翻訳しよう。</li><br /></ul><br /></li><br /></ul><br /></li><br /></ul><br /></li><br /><li>テスト作成 <br /><ul><br /><li>ゴール:ミス(タイポやロジック)を見つける <br /><ul><br /><li>やるべき事 <br /><ul><br /><li>例の入力を関数に適用しよう。</li><br /><li>出力が予測したものと同じかどうか確かめよう</li><br /></ul><br /></li><br /></ul><br /></li><br /></ul><br /></li><br /></ol><br /><p>デザインレシピはプログラムをデザインする過程において遭遇する、様々な問題を打ち倒す魔法の弾丸ではありません。デザインレシピとは開発中にしばしば起こりえる不可避なトラブルに対するある種のガイドラインなのです。レシピでもっともクリエイティブで、もっとも難しい部分は関数本体をデザインする事でしょう。この点は、参考書籍を読解する能力、数学的関係を明らかにする能力、基本的な事柄に対する知識に依存します。これらはコンピュータプログラムをする上に於いてはどれも必須ではありませんが、一方、作るべきアプリケーションによっては、専門的な知識が必要になる事がありえます。この本の残りでは、何を、どのようにして、これらの複雑な過程に対処していくかお見せしようと思っています。</p><br /><p><strong>専門知識:</strong>しばしば、問題によっては、その範疇での特殊な知識を用いて関数本体を仕上げなければならない事もあります。これらの知識を<strong>専門知識</strong>と呼びます。例えば、代数のような簡単な数学から微分方程式のような複雑な数学、あるいは、音楽、生物学、土木工学、美術等等等の非数学的な素養も必要になるかもしれません。</p><br /><p>プログラマはあらゆる分野にわたるアプリケーションの全てを知ることは不可能なので、各種専門家と問題に付いて話し合う事が出来るように、それら多方面のアプリケーション分野で使われる用語を理解出来るように努めなければなりません。それらの用語はしばしば数学でしょうが、場合によっては、特にデータ関係のアプリケーション領域ですが、自分で用語を発明しなければならない事もあります。そういう背景もあって、プログラマは可能な限り、コンピュータ用語のしっかりした理解が求められているのです。</p>cametan_001http://www.blogger.com/profile/04882440058326617382noreply@blogger.com0tag:blogger.com,1999:blog-4018043311243103708.post-84065422930625032312011-05-24T08:38:00.003+09:002012-12-07T18:25:41.196+09:00関数型言語出身者が陥りやすいOOPの罠<div dir="ltr" style="text-align: left;" trbidi="on">
いや、大した話じゃないんですけどね(笑)。そもそもプログラミングを「関数型言語」(例えばLispとか)から始めた人ってあんまいないでしょうし、言わば「何を当たり前の事を」と言うような話です。ただ、極私的なメモとして残しておきます。<br />
<br />
さて、お題。ところで、ソフトバンクから「みんなのPython」って本が出ています。他の参考資料は良く知りませんが、手元にある本ではSQLiteと言う簡易データベースと繋げる例を紹介しているのはこの本だけです。<br />
<br />
で、シンプルな例ではあるんですが、一方ここで紹介されてるコードと言うのは言わばバッチ処理で書かれてるんですね。関数でもない。単にファイルを起動してPythonインタプリタに読み込ませるだけの例になっている。要するに事実上、PythonインタプリタがUIフロントエンドになってるだけで、SQLを直接DBに打ち込むのと大して変わらないわけですよ。<br />
<br />
今考えてる事はこういう事です。ちょっとしたGUIでのDBへのフロントエンドを作りたい。要するにSQLは(言語としてはメチャクチャですが・笑)定形処理を行うとしたら、自由度が高すぎるんです。決まりきった検索結果を返したいのなら関数で隠蔽するのが良い、ってのがパターンになる。<br />
<br />
もっと言っちゃうと操作的にはGUIで充分なんです。つまり、ボタンをクリックして「決まりきった検索パターンで」結果を返してくれるに越した事がないんですね。<br />
<br />
ただし。GUIが出てくるとこれはもうオブジェクト指向の範疇なんですね(笑)。好むと好まざるに関わらず。オブジェクト指向 = GUIの為の言語実装手段、って言っても良いくらいです。事実歴史的にはほぼそうです。GUIと言うインターフェースを人が考えついて以来、この二つは密接に関連してきたんです。<br />
<br />
何故CLIによるプログラミングの学習でオブジェクト指向が理解しづらいか。これも当たり前ですよね(笑)。CLIである以上本質的にはOOPなんて要らないんですから。いたずらに「そう言うテクニックもあるんですよ」と言う余談めいた話にしかならない。当然「構造化されたスパゲティコードを書く方法」と揶揄される事になる。これは実はパラダイムが全く違うからなんですね。CLIでカッコ付けてOOPでコードなんて書く必要なんて無いんです。これはUIがシンプルであれば当然で、要するに"Keep It Simple, Stupid!!!"って言われるハメになるでしょう(笑)。<br />
<br />
しかし、ステージが「GUIをやりたい」となると話は変わってくる。今急激にOOPを克服せなアカン事になってしまった(笑)。<br />
<br />
まあこんな状態なんですけれども。<br />
<br />
もちろんOOPの概要は知っています。理屈は分かる。継承、って現象も知ってる。ただ、CLIで数値計算用のスクリプトを書いてたりしてた以上OOPの出番なんてなかった(笑)。当たり前ですよね。使い捨て前提だし、そんな「ちょっと計算して確かめたい」ってだけのブツにOOPなんて導入したら、恐らく単純に関数使って書いたコードの1.5倍(当社比)は膨れ上がります(笑)。大盛イカ焼きそばならいいですがコードが1.5倍ってなあ困りますね。<br />
<br />
特にPythonはself、self、selfなんで、「何でちょっと計算したいだけなのにselfまみれやねん」って成りかねない。いや、なる(笑)。<br />
<br />
つまり、個人的には「テキストを写経した」事はあるけど、自ら率先してOOPを使ってコードを書こう、なんて考えた事もやった事もないんですよ。ただし、GUIのフロントエンドとやり取りする以上これはいきなり必須になってしまった。少なくとも必須と考えた方が良い。将来的な拡張とかも考えるとね。<br />
<br />
ところで。いきなり話は変わりますが(笑)。PythonでSQLiteを操る場合、実はちょっとした制限があるんです。それは<br />
<br />
<blockquote>
SQL部分はトリプルクオート(例えば"""〜"""や'''〜''')で囲んだ方が良い</blockquote>
<br />
と言う部分です。まあ、制限っつーよりある種規約ですか。<br />
<br />
実はPythonではトリプルクオートは通常、一種のDocStringの役目を果たします。コメント以上のコメントですね。要するに、何らかのモジュールを作成した場合、そのモジュールの「機能を」説明する為に付ける一種のコメントです。Emacsなんかでお馴染みでしょう。Emacsも基本、それらの情報をソース本体に記述されたDocStringから引っ張ってきます。DocString。いいのかしら用語これで(笑)。まあ、いいや、多分(笑)。<br />
<br />
ちょっと簡単な例を上げます。例えば「みんなのPython」によると、DBから引っ張ってきたカーソルを使って、SQLを実行するには、Python + sqite3モジュールの組み合わせでこのように書くことが推奨されます。<br />
<br />
<script src="https://gist.github.com/987889.js?file=sample.py"></script><br />
もちろん「?」なんてのはSQLじゃあありませんよね。cursor.executeの引数はSQL文のテンプレートと後続のタプルとで成り立っていて、要するに?は後続のタプルが持ってる値を順番に埋め込んで行く為の「穴」の役割を果たしています。つまり、この引数が「評価」(笑・Lisp的解釈?)されて「完全なSQL文になってから」メソッドexcuteに渡される。とまあ、こう言う仕組みになってるんですよ。<br />
<br />
これ便利でしょうか(笑)?最初見た時「うげえ」とか思ったんです。たしかに一見便利には見えます。しかし、実際はそのSQLのテンプレート「自体」を作るのが大変なんじゃなかろうか、と。つまり、この構造を成り立たせる為にはソースコード内の関数内に「SQL自体を」埋めこまなきゃならない。と言う事はデータベースを開いて、カーソル作って、何らかのSQLを実行して、データベースを閉じる、と。そこは抽象化出来ても限界があるんじゃなかろうか。要するに、このSQLの「雛形」を保持した関数自体は全く局所的な役割しか果たせず、考えうる(必要と思われる)SQLテンプレートを保持した関数を量産しないといけない。これはメチャクチャ醜いのでは、と恐怖したんですね。<br />
<br />
そもそも関数を作るのは「汎化作用を狙って」です。一つの機能しか持たせられないのにどーしてfunctionなのか、と。<br />
<br />
<h1>
sqlite3モジュールのトリプルクオートには理由がある</h1>
<br />
さてこれは困った。何が困ったかと言うと、大体Lisp経験者はそもそもこんな「SQLを(基本的には)丸ごと埋め込む」ようなバカな作法は嫌いでしょ(笑)。絶対SQL自体をバラバラの要素に分解しちゃって、自在に組み立てられないか、って考える筈です。でしょ(笑)?コードを自動生成する・・・そうなんですよ。Lisp経験者なら恐らく絶対「マクロ」を使う局面なんです。そして汎化を狙う筈だ。<br />
<br />
LispのマクロはLispでLispコードを自動生成する事です。このケースの場合は、Pythonを使ってSQLコードを自動生成出来ないか、って事ですよね。LispはLisp内で閉じてますし、PythonでPythonを書くのは難しいでしょうが、Python <-> SQLだったらまあ、理論的には可能でしょ。基本的なアイディアは同じです。要するにカッコではなく、文字列対象にして操作が可能なら、これは立派なSQLへの翻訳機です。<br />
<br />
とっこっろっがねえ〜〜。ここで壁にぶち当たった。何故ならそもそもPythonでは<strong>トリプルクオートで囲まれた文字列を操作する事が出来ない</strong>。これにビックリしたんですね。どう工夫しても結局フツーの文字列に落ち着いちゃう。<br />
<br />
先にも書きましたけど、元々トリプルクオテーション自体はDocStringを狙ってるものです。つまり<strong>人間が手書きするのが前提</strong>なんですね。それを一種リファレンスとして扱う為の機能なんです。従ってPythonそのものでトリプルクオテーションを操作する事は考えられてません(っつーかその筈。ググったけどそんな方法は見つからなかった・笑)。<br />
<br />
大変不自由なんですよ。そして何故かsqlite3モジュールはこいつを利用してSQL文を書く事を推奨している。っつー事は必然的にSQLの部分は<strong>手書きせんとアカン</strong>って事になる。うげえ、とか思ったんですね(笑)。何じゃこのシステムは、と。<br />
<br />
んでまあ、あとで調べて分かったんですけど、これってどうやらセキュリティの為らしいんです。Pythonで操れるフツーの文字列でSQLを記述する方法を選んだ場合、要するに、クラッカーなんかが特定の悪意を持ったSQL文字列をPythonに喰わせて、データベースシステムを破壊しちゃう事も可能なんです。だから「敢えて」Pythonで操れないトリプルクオテーションをSQL埋め込みの雛形に選んだ。<br />
<br />
言われてみればなるほどな、良く考えられてるな、とは思うんですが、当初は参りましたね(笑)。トリプルクオートでわざと囲んだSQLを別ファイルに用意してロードしてみても、ファイル開くとフツーの文字列になってたりして(笑)、<br />
<br />
<blockquote>
「てめえ、何じゃこりゃ、フザケてんのか!」</blockquote>
<br />
とか無意味に怒りまくってましたもの(笑)。<br />
<br />
<h1>
関数型言語出身者がハマる罠</h1>
<br />
例えば関数型言語、特にScheme経験すると、関数は何らかのデータをフィルタリングしてその結果を返すように書くべき、それが美しいプログラミングだ、と叩き込まれます。つまり、引数で入力 -> フィルタリングした結果を返す、って事ですよね。<br />
<br />
要するに、通常の(Scheme的な)方法で考えると単に文字列を引数として与えて、内部でSELECTか何かと結合させて、その結果を返せば一丁上がり、ってなる筈なんですが、ところが出力をトリプルクオテーションの文字列として返す事が難しい。まあ、そんなワケでPythonでSQLを弄るのは最終段階にしておいて、取り敢えずじゃあ雛形のVIEWばっか作成すれば何とかなるのかしらん、と暫くRDBMSの基本機能の方に頼ってたわけです。<br />
<br />
<h1>
VIEWは必ずしも軽くない</h1>
<br />
何故そう思ったのか、と言うとPythonでフィルタリングを掛けられる状態まで、ある種SQLのVIEW上でデータを加工しておいたら、Python側のプログラム自体はシンプルで済むのではないか、と言う期待があった。<br />
<br />
ところが、VIEWにVIEWをどんどん重ねたSQLを書いていくと無茶苦茶データ読み出しが遅くなっていく(笑)。最初、ポインタ参照程度で軽いのかしらん、とか思ってたんですが、さにあらず。一回の検索で5分とかかかる例とかも出てきて「ああ、こりゃやってられんわ」と言う結論になってしまった(笑)。<br />
<br />
今回初めてRDBMSを触った、ってなくらいなんで、当然SQLも初めて。全くシステム的な理解がない。その中で、Twitterでやり取りしている間に「SQLの副問い合わせは重いですよ」と言う話が出てきた。<br />
<br />
そうなんですよね。RDBMSのVIEWってのは基本的に副問い合わせを分離したモノに過ぎない。記述の簡便化なわけです。つまり、VIEWにVIEWを重ねたような入れ子構造にすると、メチャクチャ重くなって当たり前だったんですね(笑)。副問い合わせだらけですから(笑)。<br />
<br />
つまり、部分的に必要なデータ、ってのを最終的にPythonから取り出す為に加工を始めた筈だったんですが、その「最終的に」加工する直前までの状態で、元ネタからのデータを「全部引っ張ってきたような」表を作りまくる、と。それが二重、三重にも重なっていくと、表の作成だけでメチャクチャ時間が取られて当たり前だったんです。規模的に一般にはどーなのか知りませんが、現時点対象の元ネタは6Gくらいのデータです。当然デカイんで途中経過で表の作成が何重にも重なれば大変な事になってしまいます。<br />
<br />
漸くココに来て、そしてどーやらGUIの必要性が出てきたので<br />
<br />
<blockquote>
「しゃーねえ。色々合わせてOOPでやってみるか」</blockquote>
<br />
と腹をくくらざるを得なくなったわけです。<br />
<br />
<h1>
メソッドと関数は全然違う</h1>
<br />
んでしょーがないんで、書籍でPythonのオブジェクト指向の部分読みなおしてたりしてたんですが…。サッパリちんぷんかんぷんでありんす(爆)。<br />
<br />
っつーか、いや、分かりやすいんですよ。分かりやすいんだけど、概論過ぎてピンと来ないんですよね(笑)。あまりにも簡単な例になり過ぎていて、って事があって、要するに実用的じゃない。<br />
<br />
例えば、「Pythonチュートリアル」ではこんな例が上げられています。<br />
<br />
<script src="https://gist.github.com/987889.js?file=sample1.py"></script><br />
これはPythonの作者、Guido van Rossum自らによる例です。なるほど、クラスがあって、継承して、メソッドがオーバーライドされてるのは分かる。分かるんですが……。<br />
<br />
問題は、こんなprintで出力するだけの「クラス」なんて実際にはありえないでしょう(笑)。概念は分かっても実用性がない。要するに説明のレベルとしては基礎的過ぎるんです。<br />
<br />
実際のPythonのOOPのコードはとにかくselfだらけ、です。あっちにselfアリ、こっちにselfアリ、です。そしてSchemeなんかの関数型言語出身者が一番困るのが、コードを読んでもどの変数がどの変数を参照してるのか、サッパリ分からない、って事です。<br />
<br />
ただでさえ、メソッド内定義はそれこそオブジェクト名の嵐ですしね。こっちのself.spamは唐突に出てきて何か代入されてるるんだけど、あっちに書かれてるself.spamと同じモノなの?違うものなの?ああ、分からん!となる(笑)。<br />
<br />
分かった範囲で結論から言うと、どうやら「メソッド」ってのは「関数」とはまるっきり違うモノらしい。正直言うと、どの本見ても、Python初心者への恐怖感を拭うためか、「メソッドは関数と同じようなモノ」と説明しています。しかし、これがScheme辺りからプログラミングをはじめた人間を混乱にたたき落とす(笑)。<strong>実は全く違う</strong>。表記法の簡便性としてはdefを使うのは妥当でしょうが、正直別の語を当ててほしいくらいです。<br />
<br />
いや、別にPythonが悪いわけじゃないんですよ。ただ、Scheme辺りから始めちゃうと<strong>関数とレキシカルスコープはほぼ一体だ</strong>って考えちゃうんですよね。しかしどうやら<strong>メソッドはレキシカルスコープを持たない</strong>。あるのはクラス範囲内限定の大域変数があるだけ、です。どうやらそうみたい。んでEmacs Lisp書いてる人は悩まないでしょう。Scheme上がりが混乱する、んですね(笑)。どうしても関数のスコープ基準で変数の参照を考えますから。<br />
<br />
つまり、どうやらメソッド自体が<strong>変数を内包する変数だ</strong>、って考えた方が分かりやすい。関数だ、って捉えるから混乱する。変数だったら書き換えも生じる可能性もある。<strong>オーバーライド</strong>ですよね(笑)。<br />
<br />
<h1>
継承してみる</h1>
<br />
そこまで噛み砕いてしまえば意外と簡単かも、って思いました。っつーか関数のフリした変数があるのならこれはもはや基本的には<strong>単なるバッチ処理</strong>です。Scheme上がりは如何に美しい関数を書くかに腐心する傾向があるんですが(笑)、もうこうなったら豪に入りては豪に従え、やけのヤンパチ、アジの開き直りでありんす(笑)。<br />
<br />
実は本質的にはSQLを埋め込む関数の量が増大するのでは、と言う問題は全然解決してないんですが、継承の機構を使ってSQLを実行する雛形をスーパークラスにしちゃえば良いのでは、と考えました。<br />
<br />
実は良く知らなかったんですが、DBのカーソルは基本的に、関数型言語で言う破壊的操作を伴うので、例えばcursor.exetute()でデータを取り出し変数x1に代入。その後、別のSQLを実行して変数x2に代入すると、変数x1の内容が変数x2と同じに書き換えられちゃうんですね。「んなバカな!」とか思ったんですが、どうやらそう言う仕様らしい(笑)。しかもカーソル自体はどーやらPythonの機能と言うよりはRDBMS側が提供している機能らしいんで、こりゃまたどーしよーもねえ。<br />
<br />
ただ、@kaorin_linux氏の話によると、これはクラスを作って隠蔽してしまえば意図通りの動作になる模様で、そう言う意味でもDBにアクセスする一連の作業をクラス化する事には意味があると思えます。いや、本当はもっと単刀直入な手もある、って話ですが、いずれにせよ、GUI部分とやり取りするのを視野に入れると、どの道オブジェクトとして纏めておいた方がいいでしょう。<br />
<br />
んで、初めて自発的にOOPでのコードを書いてみました。書いてみると実はなんて事なかった(笑)。以下がそれです。<br />
<br />
<script src="https://gist.github.com/987889.js?file=sample2.py"></script><br />
参考にしたページもあったんですが、データを読み出すだけ、と言う意図もあり、全然簡単に出来ましたね。<br />
<br />
クソ、くだらん(笑)。一体何を悩んでたんだ、俺は(爆)。<br />
<br />
最初、初期化で何するか、ってのも悩んだんですけど、良く考えてみれば初期化に必要なのはDBに接続する事、です。わざわざ手動でメソッド呼び出して接続したくない。だからシンプルに__init__ではデータベースの接続を行ってカーソルを作成しています。<br />
<br />
ポイントはSQL文を外部からどうやって呼び出すか。ここで先ほど見た通り、関数型言語な人ってのは関数の引数をアテにする。性質的にはさっきも書きましたが、オーバーライド自体が「破壊的変更」にしか見えないんで好ましい、とは思わないんですよ。まあ、他の人は知りませんが、僕はそうですね。<br />
<br />
ただ、さっきも言った通り「豪に入りては豪に従え」なんで、意図的に「オーバーライドされる為だけが目的の」SQLtempと言うメソッドを作っています。ここがトリプルクオートのSQL文が入る場所です。従って中身は消されるのが前提です。もちろん空の文字列を喰わせておく、って手もありますが、ここではpassを使って、明示的に「何もしない」事にしました。<br />
<br />
あとはお決まりですよね。SQL文を実行してデータを読み出し、そいつを返り値にする。もうちょっと言うと、どう言った穴開きのSQL文が来るか分からない。そこでshowAllRowsは第二引数に可変長引数を取っています。別の手もあるんですが、一応、ここの可変長引数は纏めてタプルになるので、最初に見たとおり、トリプルクオートのSQL文にそのまま渡すには大変都合が良いです。そのまま穴を埋めてくれる。んで、想定としては、ここの可変長引数はGUIのマウスのクリックから来るシグナルから取り出します。<br />
<br />
で、sqlite3モジュールのfetchall()はデータベースのデータをタプルのリストとして返してくるので、そいつを返り値にしときゃ、まあ良いのではないでしょうか(笑)。<br />
<br />
そうすれば、データベースのGUIフロントエンドで、例えば何かのデータが表示されてる。キーを表示している一部分をクリックすると、そこに関連した詳細なデータが出てくる、と言うカタチを目論む。その「詳細なデータ形式」と言うのはSQLとしてテンプレを用意しておけば良い、ってのが基本です。つまり、ここは上で作ったスーパークラスを継承した何かがあれば良いわけです。機能的にはもちろん、<br />
<br />
<ol><br />
<li>データベースに接続する</li>
<br />
<li>SQLを実行する</li>
<br />
<li>データを読み出す</li>
</ol>
<br />
なんですが、この1番と3番は既にスーパークラスで作られています。2の半分も実装済みです。繰り返しますが、要は「適切なSQLのテンプレートさえ」あれば良い。<br />
<br />
従って書くべきコードは基本的に以下の通りで、形式的にはPythonですが、事実上殆ど単なるSQLです。<br />
<br />
<script src="https://gist.github.com/987889.js?file=sample3.py"></script><br />
雛形作っておけばSQLで実行してみて、テストして、上手く行った後、ここにそれを貼りつければいい(笑)。簡単ですね(笑)。コピペでコードを仕上げる、ってのが如何にもOOP的です(笑)。Smalltalk的と言うか(笑)。<br />
<br />
まあ、てなワケで悩んでた時間も長かったんですが、やってみると思ったよりOOPで書くのはツラくはない、ってのが曲がりなりにも分かりました。もっともC++/Javaな人たちから見るとあまりにも当たり前過ぎてつまんない話でしたね(笑)。<br />
<br />
すんません(笑)。</div>
cametan_001http://www.blogger.com/profile/04882440058326617382noreply@blogger.com0tag:blogger.com,1999:blog-4018043311243103708.post-80517508877995378132010-10-05T06:46:00.004+09:002010-10-05T08:01:21.015+09:00((Pythonで) 書く (Lisp) インタプリタ)Pythonで書くSchemeインタプリタです。<br /><script src="http://gist.github.com/610454.js?file=lispy.py"></script><br />実際は、Schemeとは呼べないんですけどね(笑)。仕様満たしてませんし。かつ単なる写経です(爆)。<br />作者は<a href="http://www.amazon.co.jp/gp/product/4798118907?ie=UTF8&tag=keibawiki-22&linkCode=as2&camp=247&creative=1211&creativeASIN=4798118907">PAIP</a><img src="http://www.assoc-amazon.jp/e/ir?t=keibawiki-22&l=as2&o=9&a=4798118907" width="1" height="1" border="0" alt="" style="border:none !important; margin:0px !important;" />の著者として有名なPeter Norvig。過去はLisperとして有名でしたが、現在はPythonistaとして有名な人です。<br /><br />「Lisp以外の言語で」Lispを実装してみる、ってのは初めての経験で、結構面白いとは思いました。<br />ただ、Pythonの構文の細かいトコには明るくないんで、「読解」自体は上手く行ってるとは思いませんが(笑)。んで、やっぱOOPは嫌いです(笑)。<br /><br />ちなみに、Python2.6.xだとだいぶ改良されてはいますが、やっぱエンコーディングはめんどっちいと思っています。cametan_001http://www.blogger.com/profile/04882440058326617382noreply@blogger.com0tag:blogger.com,1999:blog-4018043311243103708.post-20421975945853387852010-07-20T09:18:00.000+09:002010-07-20T09:19:28.223+09:00Goldbach's conjecture<script src="http://gist.github.com/480612.js?file=p40.ss"></script>cametan_001http://www.blogger.com/profile/04882440058326617382noreply@blogger.com1