page0040
の編集
http://osecpu.osask.jp/wiki/?page0040
[
トップ
] [
編集
|
差分
|
バックアップ
|
添付
|
リロード
] [
新規
|
一覧
|
単語検索
|
最終更新
|
ヘルプ
]
-- 雛形とするページ --
BracketName
FormattingRules
FrontPage
Fulyn
Fulyn-v2
Fulyn_Samples
Help
InterWiki
InterWikiName
InterWikiSandBox
K
KOR_PIT8254
KWVM.NET
Liva
MANA
MenuBar
OSECPU_FPGA
PG_MANA
PHP
PukiWiki
PukiWiki/1.4
PukiWiki/1.4/Manual
PukiWiki/1.4/Manual/Plugin
PukiWiki/1.4/Manual/Plugin/A-D
PukiWiki/1.4/Manual/Plugin/E-G
PukiWiki/1.4/Manual/Plugin/H-K
PukiWiki/1.4/Manual/Plugin/L-N
PukiWiki/1.4/Manual/Plugin/O-R
PukiWiki/1.4/Manual/Plugin/S-U
PukiWiki/1.4/Manual/Plugin/V-Z
RecentDeleted
SandBox
WikiEngines
WikiName
WikiWikiWeb
YukiWiki
hikalium
hikarupsp
hikarupsp_ELCHNOS
hikarupsp_ELCHNOS_IDE
hikarupsp_FrontEndCode
hikarupsp_WebCPU-VM
hikarupsp_WebCPU-VM_internal
hikarupsp_study_hh4
impressions
impressions0000
jpag0000
jpag0001
jpag0002
jpag0003
jpag0004
jpag0005
lambdalice
mandel59
members
memo0000
memo0001
memo0002
memo0003
memo0004
memo0005
memo0006
memo0007
memo0008
memo0009
memo0010
osask
osecpu4android
page0000
page0001
page0002
page0003
page0004
page0005
page0006
page0007
page0008
page0009
page0010
page0011
page0012
page0013
page0014
page0015
page0016
page0017
page0018
page0019
page0020
page0021
page0022
page0023
page0024
page0025
page0026
page0027
page0028
page0029
page0030
page0031
page0032
page0033
page0034
page0035
page0036
page0037
page0038
page0039
page0040
page0041
page0042
page0043
page0044
page0045
page0046
page0047
page0048
page0049
page0050
page0051
page0052
page0053
page0054
page0055
page0056
page0057
page0058
page0059
page0060
page0061
page0062
page0063
page0064
page0065
page0066
page0067
page0068
page0069
page0070
page0071
page0072
page0073
page0074
page0075
page0076
page0077
page0078
page0079
page0080
page0081
page0082
page0083
page0084
page0085
page0086
page0087
page0088
page0089
page0090
page0091
page0092
page0093
page0094
page0095
page0096
page0097
page0098
page0099
page0100
page0101
page0102
page0103
page0104
page0105
page0106
page0107
page0108
page0109
pagenames
populars
seccamp2013
seccamp2014
seccamp2017
ttwilb
ttwilb-asmi
yao
* 機能密度に関するおはなし -(by [[K]], 2013.06.17) ** はいけい -もともとはとあるところへ寄稿するための文章でしたが、ちょっと時間がかかりそうだったのでこちらに載せることにします。 ** ほんぶん -こんにちは。自称「変人プログラマ」の川合秀実です。私は趣味として、コードゴルフをたしなんでおります。今日はそのことを熱く語らせていただきます。おっと、今回私は面白おかしく書きますので、まじめな内容は期待しないでくださいね。 *** コードゴルフとは? -川合流に表現してしまえば、コードゴルフは最適化の一種です。普通の最適化が処理時間の短縮を目指すのに対して、コードゴルフではプログラムを短くすることを目指します。処理時間の増加は基本的には気にしません。出力結果はもちろん同じでなければいけません。最短を目指す行為がスポーツのゴルフを連想させるため、このように呼ばれるようです。 *** コードゴルフは役立つ技術か? -明言します、役には立ちません。メモリやディスクが貴重だった時代ならいざ知らず、今どきプログラムが短くなることに何の意味があるというのでしょう。いや私の本当の気持ちを言えば、役立ちます。詳細は後述します! しかしそれはきっと共感を得られないと思います。だからここは潔く負けを認めて、役には立たないと断言することにします。 -ではコードゴルフは無価値なのでしょうか?とんでもない! 役に立たないことは無価値とは違います。 -今、自動車なら時速100kmだって簡単に出せますが、それでもオリンピックでは短距離走で、人間同士がわずかな速度差を競っています。これを無価値だと思いますか? 役に立つと言うことは難があると思いますが、価値があることに異論はありません。応援するだけでも十分に楽しいですし、夢も希望も感動もあります。登山だってそうです。ヘリコプターで登ればいいのにって言われたらなんて言い返せばいいのですか? 場合によっては遭難のリスクさえありますが、しかし人間は限界を求めて挑戦する生き物なのです。そしてそれを奨励するからこそ、人類はここまで進歩してきたのです。 -コードゴルフは芸術です。パズルでもあります。数学的な美しさもあります。あらゆる無駄をそぎ落とした後に残ったもの、それがコードゴルフなのです! コードゴルフを鑑賞できないなんて、人生の何パーセントかを損していると思います。でも大丈夫、この記事を読んでいるうちに見どころが分かってくると思います。 *** コードゴルフは役に立たない技術か? -価値があることは納得していただけたとして(笑)、では本当に役立たないのかを検証し直したいと思います。 -まず、コードゴルフのスキルと、プログラミングのスキルには一定の相関があると言われています。 -プログラミングに対する知識が多ければ多いほど、コードゴルフにおいても有利です。もちろんコードゴルフの追及に必要な知識と、一般のプログラミングスキルは完全に一致しているわけではないのですが、しかし目安にはなるのです。ですから、スキルテストとして使うことができると思います。その際、限界値にどこまで近づいたかは簡単に数値化できますので、順位を付けることも容易です。さらには、実際に最小化したコードを求めることなく、この程度の処理なら、だいたいこのくらいまで縮むはずだと、限界値を予測する競争も可能です。これを鍛えると、処理内容から実装の規模感を見積もるスキルが上がります。 -プログラミング言語の優劣の評価にコードゴルフを使うこともできます。Aというプログラミング言語はこの課題を○○バイトで記述できるが、Bというプログラム言語は最低でも××バイトを要する、とかです。 -処理速度が重要な場合はこんな指標は何の役にも立ちませんが、今はCPUも大変高速になり、たいていの処理では処理速度が問題になることはありません。そういう場合においては、時間を要しているのは演算時間ではなくて、コーディング時間です。短いプログラムはコーディング時間も短くなるでしょう(たまに可読性に難のある言語があってこの限りではないのですが、しかしそれも訓練すればスラスラ読めるのかもしれません)。したがって、短く書けるプログラミング言語は、少なくともその課題には向いていると言えると私は思います。 -プログラミング言語に限りません。OSの優劣だって比較できます。CPUの優劣だって比較できます。 -念を押しますが、重い処理をすることを考えている場合は、コードゴルフの結果なんて無意味です。その場合に役立つ比較は、速さ優先で書き比べた場合であって、それはベンチマークテストなのです。しかし軽い処理を考えている場合は、ベンチマークテストなんて無意味です。どうかここを区別していただきたいです。ここを区別できない人に限って、コードゴルフのことを悪く言うのです。 -コードゴルフは、ディスクを節約します。ダウンロード時間も節約します。もし処理時間が重要ではなくて、かつ起動頻度があまり高くないソフトウェアのすべてがコードゴルフ的に最善であれば、あなたのディスクの空き容量は相当増えるのではないかと思います。最近は特にOSが大きいですよね。Officeやブラウザも大きいですよね。もちろんこれらのうちよく使う部分もあると思いますから、それは速度優先で書かれるべきです。そうでないと実行速度が落ちてもったいないです。しかし、どんなOSやアプリであっても、一般的な傾向として、プログラムの80%は処理速度にほとんど影響がないのです。そういう部分はコードゴルフしておくべきなんです。私はそう思います。 -逆に考えてみましょう。サイズが最小ではないということは、無駄があるということです。必要がなければ、無駄があっても構わないですか? モッタイナイの精神がもしあなたにもあれば、血が騒ぎませんか? お金もエネルギーもしっかり節約しているのに、どうしてコードサイズは節約しないんですか? たるんでいると思いませんか? *** 機械語でのコードゴルフ -私は機械語のコードゴルフが大好きです。ここからはその話をしたいと思います。 -まずなぜ機械語のコードゴルフがいいのかですが、それはほかのプログラミング言語でのコードゴルフと比べて、結果がよくなるからです。つまりこうです。ある課題に対して、C言語vs機械語でそれぞれコードゴルフをして、どっちが小さく書けるか勝負すると、機械語が勝ってしまいます。いやもちろん、C言語が絶対に負けるというわけではありません。たまたま課題にぴったりはまるライブラリがあってそれを使うとC言語が圧勝みたいなこともありますが、一般的な問題でやると機械語はとても小さくなります。私としては、都合のいいときにしか勝てない言語が優れているとは思えません。 -そもそもコードゴルフはどれだけ小さく書けるかを競うわけですが、機械語以外でやっていると、「あーあ、機械語だったらもっと小さくできるんだけどなあ」とか思っちゃいます。そんな未練たっぷりでやっても面白くないわけです。もし自由に好きな言語を選んでコードゴルフしていいとしたら、みんな機械語を選ぶでしょう。それくらい機械語は最強なのです。機械語が不得意な人にはこの限りではありませんが。 -結局人間向けの普通のプログラミング言語は無駄がいっぱいなのです。機械語は単純で無駄が少ないのです。 -機械語のコードゴルフを極めても、機械語を直接扱うときくらいにしか役に立たないわけですが、現代において機械語を直接扱えてもまったく尊敬されません。というか変態扱いされます。しかし、役に立つとか立たないとか今さらそんなことを気にしてどうするんですか。大事なのは限界を極めることですよ。機械語以外で競争して勝っても、それは井の中の蛙(かわず)です。お山の大将です。役に立つかもしれませんが、それでいいんですか?きっと役立つことはまともな誰かがやってくれますから、私は心置きなく限界に挑戦することにします(笑)。 *** Hello, world!のコードゴルフ -確か2006年ごろだったでしょうか。画面に"hello, world!"を出力するためのLinuxアプリをどれだけ小さく書けるかという競技が日本で流行しました。もちろんx86の機械語で書いて、バイト数を競います。これと関係あるのかないのか、2006年にはBinary2.0カンファレンスが開かれたり、バイナリアンという言葉も生まれました。そしてこの競技はついに58バイトのプログラムを生み出して終了しました。これはLinux上での限界と言われていますし、私もその通りだと思います。 -この58バイトを世界で最初に達成した人が誰なのかわからないのですが、 --http://d.hatena.ne.jp/kikx/20061111 -に書いてあるので、「菊やん」さんかなと思っております。 -[このあたりにコンソールでの実行画面を入れる] -一方で、同じ課題をMS-DOS上でやったらどうなるでしょうか。私やその仲間たちの研究の結果、これは23バイトで書けて、そしてこれが限界だと思われます。ちなみにWindowsだと100バイト以上になってしまいます。 -これで分かることは、OSごとに結構違うということです。そして同じ課題に対してこれほどの差が出るのは、OSの優劣を反映しているのではないかと私たちは考えました。Windowsはアプリに対して無駄を強いているわけです。先にコードゴルフでOSの優劣が比較できると書いたのはまさにこのことです。 -私はこの発想を原点において、できるだけアプリケーションに無駄なことをさせないようにしたOSを開発しました。それは第二世代OSASKというもので、通称guigui01と呼ばれていました。以後ここではこれをg01と略すことにします。 -g01なら、先のアプリをたったの17バイトで記述できます。なんとMS-DOSよりも小さいです!・・・もちろんこの成果はすぐに得られたものではなく、激しいOS開発競争と、昼夜逆転をいとわない数年間の激しい研究によって得られたものです。しかし私にとっては最高に充実した期間でもありました。 -ちなみに今の私は当時の私よりもスキルか向上しているので、さらに2バイト減らせて、この課題を15バイトで記述できるOSを作れると思います。 *** ルールの重要性 -この競争の際に、競争相手といろいろなルールを作りました。というのは、ルールがなければ何でもできてしまうのです。たとえば何の引数を渡さなくても"hello, world!"を出力できしてしまう「hello専用API」をOSが持っていたらどうでしょうか。それは無敵です。 -いやそれどころか、「ファイルサイズが0バイトのアプリだったら"hello, world!"を出力する」という仕様のOSだって作ろうと思えばできてしまうわけで、それはあんまりなわけです。私たちは無駄を削る競争をしたいのであって、helloだけを小さくできるようなバカOSには興味はないのです。 -私が仲間内で決めたルールは、大雑把に言えばこんな感じでした。 --(1) シグネチャは2バイト以上とする --(2) 専用APIは作らない --(3) 一般的に役立ちそうな仕様なら(=ほかの課題のゴルフでも役立つなら)基本的に認める --(4) ちゃんと正常終了する -(1)についてはちょっと解説させてください。シグネチャというのは、アプリケーション実行ファイルの先頭につける固有のマークです。これを書かないで済ませればそれだけで2バイト「も」稼げるわけですが、その場合それが実行ファイルであることを判定する方法は、ファイルの拡張子など実行ファイルの中身以外の情報に頼ることになります。 それはけしからんということになりました。なぜなら、間違えて実行ファイルではないものに実行ファイルの拡張子をつけてしまったときに、間違いなく誤作動するからです。そういう安全装置がないOSは果たしてかっこいいOSなのか。私たちの目指しているOSなのか。そんな感じのことを夜中だったか、明け方だったかに、議論していました。 -シグネチャの必要性はいいとして、じゃあそれは1バイトでいいのか、2バイトなのか、それとも4バイトくらい必要なのかとこれもまた議論しました。結論は、1バイトでは危険性がまだ高い、4バイトはさすがにもったいない気がする、じゃあ2バイトで、ということになりました。 -ちなみに先に示したMS-DOSのhelloは、シグネチャがない形式でした。だから私たちのルール上ではペナルティを2足して25として比較するべきです。そう考えるとたいしたことはありません。最善15バイトと比較すれば、40%は無駄だったということになります。 -(4)も補足が必要かもしれません。課題となる出力をこなしたあとで、あとは野となれ山となれで運を天に任せてしまうようなアプリを許せば、終了処理を節約できてコードゴルフ的に有利かもしれませんが、それは私たちが競いたいものとは違うだろうということになりました。ということでただ出力すればいいのではなく、正常終了することも暗黙の了解として含むことにしたのです。なお、コンソール環境において正常終了するというのは、単に正常終了コードを返せばそれで済むという問題ではなく、プロンプトの表示位置も正しくなるように、必要であれば改行コードも出力してから終了するということを意味します。 *** charsのコードゴルフ for (i = 0x20; i != 0x7f; i++) { putchar(i); } -で出力されるものと同一の出力結果を求められるのが、通称charsというコードゴルフです。これはASCIIキャラクタのうち印字可能なものすべての列挙になっています。helloと違うのは、ただ文字列表示を一回呼べばそれでいいというわけにはいかないことです。明記していませんが、もちろん末尾には改行も必要です。 -[このあたりにコンソールでの実行画面を入れる] -DOSではシグネチャなしで17バイトまで行きました。フェアに比較するなら点数は19バイト相当ということになります。そしてg01では13バイトになりました。ということは、DOS版の19バイトのうちの31%はムダというわけです。 -おっとここで補足が必要かもしれません。helloといいcharsといい、本当にくだらなくて実用的ではないことでゴルフしているなと思われたかもしれません。いや全くそれはそのとおりです。面目ないです。しかしいきなり実用的な規模でゴルフしても、私にはどうしたらいいか分からなくなってしまうのです。最初は易しい問題を解き、そして少しずつ難しい問題に挑戦して、よいOSはどうあるべきか、限界はどこなのかを探っていくのです。 -私は先に機械語のコードゴルフについて、他の言語よりも短くできるから機械語がいいと書きましたが、実際charsを先のルールを守って実装したら、機械語以外のプログラミング言語で13バイト相当にするのは不可能じゃないかと思います。シグネチャを引いたら11バイトです。11文字以内でcharsが書けるプログラミング言語なんてあるんでしょうか? -この先、話の流れ上g01はもう出てこないのですが、しかしg01はx86用のOSとしてはもっともコードゴルフに有利なOSだろうと思っています。helloやchars以外でも、何を作ってもとても小さくなる傾向があります。かっこいいです(自画自賛)。 *** x86以外の機械語でコードゴルフ -今までLinuxのhelloの58バイトの話からの流れで、x86の機械語限定で話を進めてきました。しかしx86以外にもCPUのアーキテクチャはたくさんありますし、x86はコードゴルフに向いた命令体系を持っているのかというと、そんなことはありません。ここまでの話の通り、私はLinuxの最善が示されてもそこで満足はできず、OSの制約をなくして最善解を探しました。その結果x86の最善解に到達したものの、やはりそこでは満足できないのです。機械語の命令体系の制約も自由にして、真の無差別級ではどうなるのかをすごく知りたくなりました。 -そのためにはOSだけではなく機械語の命令体系も設計しなければいけません。私は理想の命令体系を設計して仮想マシンを作って、それで実際にテストしながらAPIも作り究極を探求しました。自分で言っちゃいますが、これはすごいことです。なぜって、将来たとえメジャーなOSが何に取って代わろうとも、それどころか今後どんなCPUが主流になるにせよ、私がここで追求した解は(ちゃんと限界に到達していれば)不変の真理として永遠に正しいわけです。経済動向やくだらないデファクトスタンダードをめぐる争いも、すべて超越したところに到達するわけです。超かっこいいです。ドキドキがとまりません。役に立たないという唯一にして決定的な欠点だけが本当に惜しまれます・・・。 -私の研究によれば、この究極の条件でのhelloのサイズは、15バイトであると思われます。つまり、x86での研究はすでに限界に達していたように思います。そしてcharsはなんと8バイトなのです。g01の13バイトでも自分としては相当によくできていると思っていたのですが、さらに5バイトも縮むとは・・・。というか8バイトってシグネチャを引いたら6バイトですよ。6バイトでこの処理が記述できるってにわかに信じられますか?コードゴルファーならどうしても信じられなくて、ルールを破ってズル用のCPU命令かAPIがあるんじゃないかと疑わずにはいられないはずです。・・・詳細は後ほど。 -この結果はx86の機械語の命令体系にも無駄が多く残っていたという明白な証明になっています。 -helloとかcharsはコンソールアプリで、はっきりいって見栄えがしません。じゃあグラフィカルなアプリではどうでしょうか?ええ、もちろん小さくなります。ズルはないので不得意分野はありません。 -以下のような画像を描画するプログラムは13バイトで書けます。~ http://osecpu.osask.jp/download/app0020b.jpg -以下のような画像の場合は71バイトで書けます。これはすごいのかすごくないのか、ちょっと判断に困るかもしれません。参考までに書くと、この模様を既存のCPU+OSで一番小さくかけるのは、NEC PC-9801上のMS-DOSでした。しかしそれでも114バイトを要していました。71バイトが究極の答えだとしたら、その114バイトのうちの37%は無駄だったというわけです。~ http://osecpu.osask.jp/download/bball.png -[このあたりで今までのサイズ比較表を入れる] *** charsの美しさ -それではcharsの改良過程を比較して、鑑賞する事にしましょう。・・・ここから16進数が出てきますが、個々の数字の意味がよくわからなくてもあまり気にせずに、話の流れを追ってください。16進数は2桁でちょうど1バイトです。 -まずMS-DOS用の.COM形式でごく普通にcharsを作ると、以下のような23バイトになります。 B402 // 機能番号=2(一文字表示API) B220 // A=32(文字コード) CD21 // MS-DOSのシステムコール命令(Aの文字が表示される) FEC2 // Aに1を加える 80FA7F // Aと127を比較 75F7 // 等しくなければCD21のところへ戻って繰り返し B20A // A=10(文字コード:改行) CD21 // MS-DOSのシステムコール命令(改行される) B44C // 機能番号=76(プログラムの終了API) B000 // 終了コード=0 CD21 // MS-DOSのシステムコール命令(終了する) -このままでも他のOSと比べれば十分に小さくて無駄が少なくて美しいのですが、より小さくするために、以下のような改変を加えます。 --(1) x86の仕様によると、この文脈では FEC2 の代わりに 42 を使うことができる --(2) MS-DOSの仕様によると、この文脈では C3 という1バイトの命令だけでプログラムの終了処理をすることができる -その結果は以下のとおりです。これが17バイトで、MS-DOSにおける最小のcharsです。 B402 // 機能番号=2(一文字表示API) B220 // A=32(文字コード) CD21 // MS-DOSのシステムコール命令(Aの文字が表示される) 42 // Aに1を加える 80FA7F // Aと127を比較 75F7 // 等しくなければCD21のところへ戻って繰り返し B20A // A=10(文字コード:改行) CD21 // MS-DOSのシステムコール命令(改行される) C3 // プログラムの終了 -うーん、美しい。・・・え、この美しさが分からないですか?これ以上削れないというところが美しいんですよ。・・・まあいいです。 -次はOSECPU(おせくぷ、OSの名前)の例です。8バイトのcharsへの道です。最初はやっぱり工夫しないで普通に書いた場合からはじめます。 05E1 // シグネチャ 20C20 // A=32(文字コード) 1 // ラベル1 2B0750FF07 // 機能番号=65287(文字列表示API) 2132B11CF4B11B1 // メモリを1バイト確保する 48D890B130 // Aを確保したメモリへ書き込む 43B019EBFA8411CFE10 // OSECPUのシステムコール命令 CF5B11B1 // メモリを1バイト開放する 9401 // Aに1を加える A10C7F6 // Aと127を比較して等しくなければラベル1へ戻って繰り返し 208A // A=10(文字コード) 2132B11CF4B11B1 // メモリを1バイト確保する 48D890B130 // Aを確保したメモリへ書き込む 43B019EBFA8411CFE10 // OSECPUのシステムコール命令 CF5B11B1 // メモリを1バイト開放する 2B0750FF06 // 機能番号=65286 2B10 // 終了コード=0 43B019EBFA8411CFE10 // OSECPUのシステムコール命令 -なんかちょっと見ただけでもすごく長そうです。はい、なんと86バイトにもなります。これはひどいです。これはもっとも基本的な命令だけを組み合わせて作ったので、どうしても長くなってしまいます。ということで改良です。 --(3) 43B019EBFA8411CFE10 は BFA8 という短縮形に置き換える --(4) 20C20-1-...-9401-A10C7F6 は 60C20C7F...87 という短縮形に置き換える --(5) プログラムの末尾の正常終了処理は全て省略できるので書かないことにする --(6) プログラム最後の改行処理も省略できるので書かないことにする -そうするとこうなります。 05E1 // シグネチャ 60C20C7F // A=32~126までの繰り返し範囲開始 2B0750FF07 // 機能番号=65287(文字列表示API) 2132B11CF4B11B1 // メモリを1バイト確保する 48D890B130 // Aを確保したメモリへ書き込む BFA8 // OSECPUのシステムコール命令 CF5B11B1 // メモリを1バイト開放する 87 // 繰返し範囲終了 -これで31バイトです。かなり短くなりました。まだ8バイトではありませんが。 -ここで説明が必要だと思うので書きます。まず(3)の短縮形はずるいと思われるかもしれません。しかしx86でもCD21というのは実は短縮形で、基本的な命令だけで書いたら10バイト(20桁)以上を要する命令です。ですからこれは「おあいこ」なのです。(4)に相当する短縮形はx86にはありません。私はこれはシステムコール命令以上に頻出な パターンだと思っているのですが、x86の設計者はそうは思わなかったのかもしれません。コードゴルフ的な設計力に関しては私の勝ちですね。(5)はOSの仕様の勝負で、(2)に相当する仕様です。(6)もOSの仕様です。 -さて、このプログラムはまだまだ小さくできます。OSECPUではAPIと頻出する関数呼び出しについて、超短縮形を用意しています。5で始まる命令群です。これはAPIと関数呼び出しのパターンを分析して、パラメータの渡し方をいくつかのタイプに分類して整理して設計したものです。g01もこれとほぼ同じ仕組みを持っています。これを使うとcharsは劇的に短くなります。 --(7) 2B0750FF07-2132B11CF4B11B1-48D890B130-BFA8-CF5B11B1 は 511B00 という短縮形に置き換える 05E1 // シグネチャ 60C20C7F // A=32~126までの繰り返し範囲開始 514B00 // Aの文字を文字列表示APIを使って表示 87 // 繰返し範囲終了 -これで10バイトです。一気に8バイトが見えてきました。さらに以下の省略ルールを使って8バイトにします。 --(8) プログラム末尾の87は省略してよい --(9) プログラムが00で終わる場合、これも省略してよい 05E1 // シグネチャ 60C20C7F // A=32~126までの繰り返し範囲開始 514B // Aの文字を文字列表示APIを使って表示 -ついに8バイトになりました。無駄がなくて美しいです! -(7)の是非はいったん保留にして、(8)と(9)について説明したいと思います。OSECPUでは、プログラムが中途半端な終わり方をしていると判断すると、つじつまを合わせようとする機能を持っています。(8)や(9)はその機能を利用したものです。この手の機能には一部に強い抵抗があることも分かります。なぜなら、プログラマのミスによって中途半端になっているかも知れず、それをエラーも警告もなしに勝手に推測して修復するのはけしからん、バグ発見を困難にする、という主張も可能だからです。 -私はこれに対し次のような見解を持っています。まずソースレベルではこのような省略は許していません。これはまさにその手のミスを防止するためです。しかしそれらのチェックを全てパスした後で、機械語を出力するときにOSECPUが推定修復可能な省略を適用します。プログラマがこれらに気を使わなくてよくなっているのです。もはやバグ発見を困難にする問題がないので、この機能は純粋に便利で賢い機能になっています。 -以上の説明の流れで感じてもらえたと思いますが、コードゴルフは、短く書ける代替表記を探してそれを適用していくことがメインです。つまりコードゴルフに強いOSとは、多くのプログラム中に表れる頻出パターンに対して、短くする方法を十分に用意しておいただけのことだと思っています。OSECPUの場合、頻出パターンを短くするために基本命令はやや長くなる傾向があります(短い書き方を短縮用にとっておいているため)。これをずるいと思う人もいるかもしれません。それも一つの見解です。しかしこれは非常に効果のある手法ですし、私はよい方法だと思っています。 -そして(7)ですが、これも短縮形なのです。それに尽きます。51が文字列表示API呼び出しの短縮形であることを表して、その次の4が短縮形のタイプを表して、B0が変数Aを、そして最後の0が文字列の終端を表しているのです。 *** こだわりのポイント -私のこだわりポイントについて話す前に、まずはコードゴルフに関する状況を紹介したいと思います。 -今のところ、私がライバルだと感じている人は世界に一人しかいません。他にも一生懸命にやっている人がいるのかもしれませんが、私はまだ知りません(すみません!)。これを読んで「なーんだ」と思うかもしれません。「じゃあ二人で低レベルな争いをしているんだな」と思うかもしれません。 -しかしそれはたぶん大きな間違いです。私たちはたった二人ながらも、十分にレベルの高い競争をしてきたのです。私たちはどちらもOSの開発経験があり、独自OSをいくつも作ってきました。そんなプログラマはそんなに多くはありません。ですから腕前はそれなりにあるのです。そしてお互いに何度も追い抜きあっているうちに、ついに世界広しと言えどもこれを越えるものを作れる者は私たち二人以外にはいないだろう、と思えるところにまで来てしまったのです。中二病的だと思われるかもしれませんし、まあ実際そうかもしれないですが(笑)、まあやれるものならやってみてほしいです。 -ということで、完全無差別級のコードゴルフは日本が最高レベルです。しかも圧倒的に最高レベルです。確信しています。これくらいのことを簡単に断言できてしまうくらいに、私たちは研究に研究を重ねてきたのです。・・・もし私たちがどうやってやっているのか知らないのに同じところまでできちゃうすごい人がいたら(それはつまり私たちと同格ということですが)、絶対に友達になりたいです。というか私たちのノウハウを学んでさらに改良してくれる人だって十分にすごいと思うので、やっぱり友達になりたいです。 -きっと誰も賛同してくれないのを分かってて言っていますが(笑)、日本人はコードゴルファーをもっと尊敬してもいいのではないかと思います。だって世界一ですよ。まあくだらない趣味だと言われればそれまでですが、でも世界一ですよ。そう簡単になれるものじゃないんです。日本が世界一になれる分野ってそんなにたくさんあるわけじゃないです。世界一を大事に思ってほしいです。・・・まあ思ってもらえなくても私はがんばりますけどね。 -現在charsに関しては世界記録は私が保持している8バイトです(自称)。しかし、競争相手は11バイトを達成しております(しかも私より先に)。私は今回chars最小記録を奪還したのです。でもその差はたったの3バイトしかないのです。油断はできません。 -先のcharsの鑑賞のところで、私はたった1バイトを削っただけでもかなりの満足感を得ていたことが感じられると思います。それはなぜかと言えば、ライバルに勝つためです。私たちの差は本当に小さいのです。ちょっと油断したら、すぐに追い抜かれてしまうのです。世界一とはそういうものなのです。 -自分でも、何もここまでやらなくても・・・と思うことはよくあります。まあ1バイトくらいいいじゃないかとか思うこともあります。きっと仲良しのライバルだってここまではやらないだろうと思うこともあります。しかしそのたびに思うのです、将来海外の人たちがこの分野に興味を持って、節操なく頑張って世界一を取られたらどうしようと。そしてそのときになって「そのアイデアなら自分も1年以上も前から持っていたんだ、でもやりすぎだと思っていたからやらないんでいたんだ」と言ってみたところで、それは負け犬の遠吠えにしかならないだろうと。 -私は今までずっと競争してきた仲良しのライバルになら負けてもいいんです。彼に負けるのは当然というか、納得できるのです。しかしそれ以外の人に負けるのは到底容認できません。もし負けないようにするチャンスが十分にあったのにそれをしなかったせいで負けたのだとしたら、きっと一生後悔します。だから私はやるのです。たったの1バイトでも取れるものはすべて取るのです。 -そもそもコードゴルフの美学は何ですか。そう、無駄がないことです。削れる部分が残っているようでは美しいなんて言えません。削れると分かっていて削らないという選択はあり得ません。美しくなければコードゴルフしている意味がないからです。 -余談ですが、日本人はコードゴルフに向いているような気がします。なんかこう、小さい物を緻密に作るのは、日本人の得意分野のような気がしませんか? そしてコードゴルフをやると、1バイトの本当の価値が分かるようになります。 -先の例ではどんどん削って短くしていく方向でのコードゴルフをご覧に入れましたが、逆の発想も可能です。まず、絶対に削れないものは何かを考えるのです。helloの場合ですと、"hello, world!"の文字列を削ることはできません。これを削ってしまうと、結局それはhello専用APIを持っているということになるからです。任意の文字列表示をコンパクトにできるものこそ究極なのです(まあhelloやworldなどの基本的な英単語に番号を振ってOSに対して単語番号で表示できるようにするような方法はありだとは思いますが)。 -charsの場合ですと、32と127(20と7F)は削れない情報だと思います。もちろんデータだけではダメです。これらのデータをそのまま表示するのか、それとも開始値や終了値を表しているのかなど、そういうことを示す制御用の情報も必要でしょう。こうして必要なものだけを積み上げていって、究極解に到達することも可能だと思います。私はこの方向で自分のプログラムを解説できるだけの能力がまだないので、この話は今回はここまでです。 -これは当たり前の事実ですが、charsを1バイト小さくするためには、OSに機能を追加しなければなりません。それは当然OSが大きくなってしまうことを意味しますが、その増加量は1バイトではなく、もっとたくさん増えます。つまりOSの大きさとcharsの大きさの合計を見れば、増加以外の何物でもありません。短縮形のサポートにしても、OS側には短縮形と展開形の両方のデータを内部には持っているので、やはりOSのサイズは増えてしまいます。そう考えると、そもそもOS改造も許した無差別コードゴルフって一体なんなのかという気がしてくるかもしれません。 -でもこう考えてみてください。OSの改良によって小さくなるアプリはcharsだけではないのです。他のアプリも小さくなるのです。もしOSが100バイト増加しても、100個以上のアプリが1バイト小さくなるのなら、トータルでは得と言えるではないですか。私たちはまさにそういう観点で考えているのです。実際私はたくさんのアプリを書いてみて、感じをつかみながらOSを改良してきました。ある機能を導入すると、アプリによって大きく なったり小さくなったりまちまちなことがありました。そんなときは、その機能をOSに入れるかどうかで悩むわけですが、その時の基準は、結局トータルではどうなのかということです。将来このOSのアプリ数が1000とかになったとして、その時にどのくらいの割合のアプリがどのくらいの影響を受けそうなのか。それを勘と経験で見積もって取捨選択していくのです。 -とにかく一番やっちゃいけないのは、charsしか小さくならないような機能を入れてしまうことです。それはhello専用APIと同じことです。だからルール違反なのです。というか私の誇りが許しません。特定のアプリしか小さくならない機能なんて全く必要ありません。OSがそれ以上に大きくなってしまうかもしれないとか、そんなのは全部関係なくてそもそもダメなのです。 *** おわりに -さてコードゴルフの世界が少しは分かっていただけたでしょうか。面白いとは思ってもらえないのは分かっています。説明も難解だったと思います。ですからそこは共感してもらえなくていいのです。とにかく私が伝えたかったのは、たかがコードゴルフにここまで熱中している人がいるんだということなのです! ** ほそく -この文章で出てくる「競争相手」はもちろんneriさんのことです! -文中で「私たち」という言葉が何度か出ていますが、それはあるときにはOSASKコミュニティを指し、またあるときは私と競争相手のことを指しているので、文脈によって違います。 * こめんと欄 #comment
タイムスタンプを変更しない
* 機能密度に関するおはなし -(by [[K]], 2013.06.17) ** はいけい -もともとはとあるところへ寄稿するための文章でしたが、ちょっと時間がかかりそうだったのでこちらに載せることにします。 ** ほんぶん -こんにちは。自称「変人プログラマ」の川合秀実です。私は趣味として、コードゴルフをたしなんでおります。今日はそのことを熱く語らせていただきます。おっと、今回私は面白おかしく書きますので、まじめな内容は期待しないでくださいね。 *** コードゴルフとは? -川合流に表現してしまえば、コードゴルフは最適化の一種です。普通の最適化が処理時間の短縮を目指すのに対して、コードゴルフではプログラムを短くすることを目指します。処理時間の増加は基本的には気にしません。出力結果はもちろん同じでなければいけません。最短を目指す行為がスポーツのゴルフを連想させるため、このように呼ばれるようです。 *** コードゴルフは役立つ技術か? -明言します、役には立ちません。メモリやディスクが貴重だった時代ならいざ知らず、今どきプログラムが短くなることに何の意味があるというのでしょう。いや私の本当の気持ちを言えば、役立ちます。詳細は後述します! しかしそれはきっと共感を得られないと思います。だからここは潔く負けを認めて、役には立たないと断言することにします。 -ではコードゴルフは無価値なのでしょうか?とんでもない! 役に立たないことは無価値とは違います。 -今、自動車なら時速100kmだって簡単に出せますが、それでもオリンピックでは短距離走で、人間同士がわずかな速度差を競っています。これを無価値だと思いますか? 役に立つと言うことは難があると思いますが、価値があることに異論はありません。応援するだけでも十分に楽しいですし、夢も希望も感動もあります。登山だってそうです。ヘリコプターで登ればいいのにって言われたらなんて言い返せばいいのですか? 場合によっては遭難のリスクさえありますが、しかし人間は限界を求めて挑戦する生き物なのです。そしてそれを奨励するからこそ、人類はここまで進歩してきたのです。 -コードゴルフは芸術です。パズルでもあります。数学的な美しさもあります。あらゆる無駄をそぎ落とした後に残ったもの、それがコードゴルフなのです! コードゴルフを鑑賞できないなんて、人生の何パーセントかを損していると思います。でも大丈夫、この記事を読んでいるうちに見どころが分かってくると思います。 *** コードゴルフは役に立たない技術か? -価値があることは納得していただけたとして(笑)、では本当に役立たないのかを検証し直したいと思います。 -まず、コードゴルフのスキルと、プログラミングのスキルには一定の相関があると言われています。 -プログラミングに対する知識が多ければ多いほど、コードゴルフにおいても有利です。もちろんコードゴルフの追及に必要な知識と、一般のプログラミングスキルは完全に一致しているわけではないのですが、しかし目安にはなるのです。ですから、スキルテストとして使うことができると思います。その際、限界値にどこまで近づいたかは簡単に数値化できますので、順位を付けることも容易です。さらには、実際に最小化したコードを求めることなく、この程度の処理なら、だいたいこのくらいまで縮むはずだと、限界値を予測する競争も可能です。これを鍛えると、処理内容から実装の規模感を見積もるスキルが上がります。 -プログラミング言語の優劣の評価にコードゴルフを使うこともできます。Aというプログラミング言語はこの課題を○○バイトで記述できるが、Bというプログラム言語は最低でも××バイトを要する、とかです。 -処理速度が重要な場合はこんな指標は何の役にも立ちませんが、今はCPUも大変高速になり、たいていの処理では処理速度が問題になることはありません。そういう場合においては、時間を要しているのは演算時間ではなくて、コーディング時間です。短いプログラムはコーディング時間も短くなるでしょう(たまに可読性に難のある言語があってこの限りではないのですが、しかしそれも訓練すればスラスラ読めるのかもしれません)。したがって、短く書けるプログラミング言語は、少なくともその課題には向いていると言えると私は思います。 -プログラミング言語に限りません。OSの優劣だって比較できます。CPUの優劣だって比較できます。 -念を押しますが、重い処理をすることを考えている場合は、コードゴルフの結果なんて無意味です。その場合に役立つ比較は、速さ優先で書き比べた場合であって、それはベンチマークテストなのです。しかし軽い処理を考えている場合は、ベンチマークテストなんて無意味です。どうかここを区別していただきたいです。ここを区別できない人に限って、コードゴルフのことを悪く言うのです。 -コードゴルフは、ディスクを節約します。ダウンロード時間も節約します。もし処理時間が重要ではなくて、かつ起動頻度があまり高くないソフトウェアのすべてがコードゴルフ的に最善であれば、あなたのディスクの空き容量は相当増えるのではないかと思います。最近は特にOSが大きいですよね。Officeやブラウザも大きいですよね。もちろんこれらのうちよく使う部分もあると思いますから、それは速度優先で書かれるべきです。そうでないと実行速度が落ちてもったいないです。しかし、どんなOSやアプリであっても、一般的な傾向として、プログラムの80%は処理速度にほとんど影響がないのです。そういう部分はコードゴルフしておくべきなんです。私はそう思います。 -逆に考えてみましょう。サイズが最小ではないということは、無駄があるということです。必要がなければ、無駄があっても構わないですか? モッタイナイの精神がもしあなたにもあれば、血が騒ぎませんか? お金もエネルギーもしっかり節約しているのに、どうしてコードサイズは節約しないんですか? たるんでいると思いませんか? *** 機械語でのコードゴルフ -私は機械語のコードゴルフが大好きです。ここからはその話をしたいと思います。 -まずなぜ機械語のコードゴルフがいいのかですが、それはほかのプログラミング言語でのコードゴルフと比べて、結果がよくなるからです。つまりこうです。ある課題に対して、C言語vs機械語でそれぞれコードゴルフをして、どっちが小さく書けるか勝負すると、機械語が勝ってしまいます。いやもちろん、C言語が絶対に負けるというわけではありません。たまたま課題にぴったりはまるライブラリがあってそれを使うとC言語が圧勝みたいなこともありますが、一般的な問題でやると機械語はとても小さくなります。私としては、都合のいいときにしか勝てない言語が優れているとは思えません。 -そもそもコードゴルフはどれだけ小さく書けるかを競うわけですが、機械語以外でやっていると、「あーあ、機械語だったらもっと小さくできるんだけどなあ」とか思っちゃいます。そんな未練たっぷりでやっても面白くないわけです。もし自由に好きな言語を選んでコードゴルフしていいとしたら、みんな機械語を選ぶでしょう。それくらい機械語は最強なのです。機械語が不得意な人にはこの限りではありませんが。 -結局人間向けの普通のプログラミング言語は無駄がいっぱいなのです。機械語は単純で無駄が少ないのです。 -機械語のコードゴルフを極めても、機械語を直接扱うときくらいにしか役に立たないわけですが、現代において機械語を直接扱えてもまったく尊敬されません。というか変態扱いされます。しかし、役に立つとか立たないとか今さらそんなことを気にしてどうするんですか。大事なのは限界を極めることですよ。機械語以外で競争して勝っても、それは井の中の蛙(かわず)です。お山の大将です。役に立つかもしれませんが、それでいいんですか?きっと役立つことはまともな誰かがやってくれますから、私は心置きなく限界に挑戦することにします(笑)。 *** Hello, world!のコードゴルフ -確か2006年ごろだったでしょうか。画面に"hello, world!"を出力するためのLinuxアプリをどれだけ小さく書けるかという競技が日本で流行しました。もちろんx86の機械語で書いて、バイト数を競います。これと関係あるのかないのか、2006年にはBinary2.0カンファレンスが開かれたり、バイナリアンという言葉も生まれました。そしてこの競技はついに58バイトのプログラムを生み出して終了しました。これはLinux上での限界と言われていますし、私もその通りだと思います。 -この58バイトを世界で最初に達成した人が誰なのかわからないのですが、 --http://d.hatena.ne.jp/kikx/20061111 -に書いてあるので、「菊やん」さんかなと思っております。 -[このあたりにコンソールでの実行画面を入れる] -一方で、同じ課題をMS-DOS上でやったらどうなるでしょうか。私やその仲間たちの研究の結果、これは23バイトで書けて、そしてこれが限界だと思われます。ちなみにWindowsだと100バイト以上になってしまいます。 -これで分かることは、OSごとに結構違うということです。そして同じ課題に対してこれほどの差が出るのは、OSの優劣を反映しているのではないかと私たちは考えました。Windowsはアプリに対して無駄を強いているわけです。先にコードゴルフでOSの優劣が比較できると書いたのはまさにこのことです。 -私はこの発想を原点において、できるだけアプリケーションに無駄なことをさせないようにしたOSを開発しました。それは第二世代OSASKというもので、通称guigui01と呼ばれていました。以後ここではこれをg01と略すことにします。 -g01なら、先のアプリをたったの17バイトで記述できます。なんとMS-DOSよりも小さいです!・・・もちろんこの成果はすぐに得られたものではなく、激しいOS開発競争と、昼夜逆転をいとわない数年間の激しい研究によって得られたものです。しかし私にとっては最高に充実した期間でもありました。 -ちなみに今の私は当時の私よりもスキルか向上しているので、さらに2バイト減らせて、この課題を15バイトで記述できるOSを作れると思います。 *** ルールの重要性 -この競争の際に、競争相手といろいろなルールを作りました。というのは、ルールがなければ何でもできてしまうのです。たとえば何の引数を渡さなくても"hello, world!"を出力できしてしまう「hello専用API」をOSが持っていたらどうでしょうか。それは無敵です。 -いやそれどころか、「ファイルサイズが0バイトのアプリだったら"hello, world!"を出力する」という仕様のOSだって作ろうと思えばできてしまうわけで、それはあんまりなわけです。私たちは無駄を削る競争をしたいのであって、helloだけを小さくできるようなバカOSには興味はないのです。 -私が仲間内で決めたルールは、大雑把に言えばこんな感じでした。 --(1) シグネチャは2バイト以上とする --(2) 専用APIは作らない --(3) 一般的に役立ちそうな仕様なら(=ほかの課題のゴルフでも役立つなら)基本的に認める --(4) ちゃんと正常終了する -(1)についてはちょっと解説させてください。シグネチャというのは、アプリケーション実行ファイルの先頭につける固有のマークです。これを書かないで済ませればそれだけで2バイト「も」稼げるわけですが、その場合それが実行ファイルであることを判定する方法は、ファイルの拡張子など実行ファイルの中身以外の情報に頼ることになります。 それはけしからんということになりました。なぜなら、間違えて実行ファイルではないものに実行ファイルの拡張子をつけてしまったときに、間違いなく誤作動するからです。そういう安全装置がないOSは果たしてかっこいいOSなのか。私たちの目指しているOSなのか。そんな感じのことを夜中だったか、明け方だったかに、議論していました。 -シグネチャの必要性はいいとして、じゃあそれは1バイトでいいのか、2バイトなのか、それとも4バイトくらい必要なのかとこれもまた議論しました。結論は、1バイトでは危険性がまだ高い、4バイトはさすがにもったいない気がする、じゃあ2バイトで、ということになりました。 -ちなみに先に示したMS-DOSのhelloは、シグネチャがない形式でした。だから私たちのルール上ではペナルティを2足して25として比較するべきです。そう考えるとたいしたことはありません。最善15バイトと比較すれば、40%は無駄だったということになります。 -(4)も補足が必要かもしれません。課題となる出力をこなしたあとで、あとは野となれ山となれで運を天に任せてしまうようなアプリを許せば、終了処理を節約できてコードゴルフ的に有利かもしれませんが、それは私たちが競いたいものとは違うだろうということになりました。ということでただ出力すればいいのではなく、正常終了することも暗黙の了解として含むことにしたのです。なお、コンソール環境において正常終了するというのは、単に正常終了コードを返せばそれで済むという問題ではなく、プロンプトの表示位置も正しくなるように、必要であれば改行コードも出力してから終了するということを意味します。 *** charsのコードゴルフ for (i = 0x20; i != 0x7f; i++) { putchar(i); } -で出力されるものと同一の出力結果を求められるのが、通称charsというコードゴルフです。これはASCIIキャラクタのうち印字可能なものすべての列挙になっています。helloと違うのは、ただ文字列表示を一回呼べばそれでいいというわけにはいかないことです。明記していませんが、もちろん末尾には改行も必要です。 -[このあたりにコンソールでの実行画面を入れる] -DOSではシグネチャなしで17バイトまで行きました。フェアに比較するなら点数は19バイト相当ということになります。そしてg01では13バイトになりました。ということは、DOS版の19バイトのうちの31%はムダというわけです。 -おっとここで補足が必要かもしれません。helloといいcharsといい、本当にくだらなくて実用的ではないことでゴルフしているなと思われたかもしれません。いや全くそれはそのとおりです。面目ないです。しかしいきなり実用的な規模でゴルフしても、私にはどうしたらいいか分からなくなってしまうのです。最初は易しい問題を解き、そして少しずつ難しい問題に挑戦して、よいOSはどうあるべきか、限界はどこなのかを探っていくのです。 -私は先に機械語のコードゴルフについて、他の言語よりも短くできるから機械語がいいと書きましたが、実際charsを先のルールを守って実装したら、機械語以外のプログラミング言語で13バイト相当にするのは不可能じゃないかと思います。シグネチャを引いたら11バイトです。11文字以内でcharsが書けるプログラミング言語なんてあるんでしょうか? -この先、話の流れ上g01はもう出てこないのですが、しかしg01はx86用のOSとしてはもっともコードゴルフに有利なOSだろうと思っています。helloやchars以外でも、何を作ってもとても小さくなる傾向があります。かっこいいです(自画自賛)。 *** x86以外の機械語でコードゴルフ -今までLinuxのhelloの58バイトの話からの流れで、x86の機械語限定で話を進めてきました。しかしx86以外にもCPUのアーキテクチャはたくさんありますし、x86はコードゴルフに向いた命令体系を持っているのかというと、そんなことはありません。ここまでの話の通り、私はLinuxの最善が示されてもそこで満足はできず、OSの制約をなくして最善解を探しました。その結果x86の最善解に到達したものの、やはりそこでは満足できないのです。機械語の命令体系の制約も自由にして、真の無差別級ではどうなるのかをすごく知りたくなりました。 -そのためにはOSだけではなく機械語の命令体系も設計しなければいけません。私は理想の命令体系を設計して仮想マシンを作って、それで実際にテストしながらAPIも作り究極を探求しました。自分で言っちゃいますが、これはすごいことです。なぜって、将来たとえメジャーなOSが何に取って代わろうとも、それどころか今後どんなCPUが主流になるにせよ、私がここで追求した解は(ちゃんと限界に到達していれば)不変の真理として永遠に正しいわけです。経済動向やくだらないデファクトスタンダードをめぐる争いも、すべて超越したところに到達するわけです。超かっこいいです。ドキドキがとまりません。役に立たないという唯一にして決定的な欠点だけが本当に惜しまれます・・・。 -私の研究によれば、この究極の条件でのhelloのサイズは、15バイトであると思われます。つまり、x86での研究はすでに限界に達していたように思います。そしてcharsはなんと8バイトなのです。g01の13バイトでも自分としては相当によくできていると思っていたのですが、さらに5バイトも縮むとは・・・。というか8バイトってシグネチャを引いたら6バイトですよ。6バイトでこの処理が記述できるってにわかに信じられますか?コードゴルファーならどうしても信じられなくて、ルールを破ってズル用のCPU命令かAPIがあるんじゃないかと疑わずにはいられないはずです。・・・詳細は後ほど。 -この結果はx86の機械語の命令体系にも無駄が多く残っていたという明白な証明になっています。 -helloとかcharsはコンソールアプリで、はっきりいって見栄えがしません。じゃあグラフィカルなアプリではどうでしょうか?ええ、もちろん小さくなります。ズルはないので不得意分野はありません。 -以下のような画像を描画するプログラムは13バイトで書けます。~ http://osecpu.osask.jp/download/app0020b.jpg -以下のような画像の場合は71バイトで書けます。これはすごいのかすごくないのか、ちょっと判断に困るかもしれません。参考までに書くと、この模様を既存のCPU+OSで一番小さくかけるのは、NEC PC-9801上のMS-DOSでした。しかしそれでも114バイトを要していました。71バイトが究極の答えだとしたら、その114バイトのうちの37%は無駄だったというわけです。~ http://osecpu.osask.jp/download/bball.png -[このあたりで今までのサイズ比較表を入れる] *** charsの美しさ -それではcharsの改良過程を比較して、鑑賞する事にしましょう。・・・ここから16進数が出てきますが、個々の数字の意味がよくわからなくてもあまり気にせずに、話の流れを追ってください。16進数は2桁でちょうど1バイトです。 -まずMS-DOS用の.COM形式でごく普通にcharsを作ると、以下のような23バイトになります。 B402 // 機能番号=2(一文字表示API) B220 // A=32(文字コード) CD21 // MS-DOSのシステムコール命令(Aの文字が表示される) FEC2 // Aに1を加える 80FA7F // Aと127を比較 75F7 // 等しくなければCD21のところへ戻って繰り返し B20A // A=10(文字コード:改行) CD21 // MS-DOSのシステムコール命令(改行される) B44C // 機能番号=76(プログラムの終了API) B000 // 終了コード=0 CD21 // MS-DOSのシステムコール命令(終了する) -このままでも他のOSと比べれば十分に小さくて無駄が少なくて美しいのですが、より小さくするために、以下のような改変を加えます。 --(1) x86の仕様によると、この文脈では FEC2 の代わりに 42 を使うことができる --(2) MS-DOSの仕様によると、この文脈では C3 という1バイトの命令だけでプログラムの終了処理をすることができる -その結果は以下のとおりです。これが17バイトで、MS-DOSにおける最小のcharsです。 B402 // 機能番号=2(一文字表示API) B220 // A=32(文字コード) CD21 // MS-DOSのシステムコール命令(Aの文字が表示される) 42 // Aに1を加える 80FA7F // Aと127を比較 75F7 // 等しくなければCD21のところへ戻って繰り返し B20A // A=10(文字コード:改行) CD21 // MS-DOSのシステムコール命令(改行される) C3 // プログラムの終了 -うーん、美しい。・・・え、この美しさが分からないですか?これ以上削れないというところが美しいんですよ。・・・まあいいです。 -次はOSECPU(おせくぷ、OSの名前)の例です。8バイトのcharsへの道です。最初はやっぱり工夫しないで普通に書いた場合からはじめます。 05E1 // シグネチャ 20C20 // A=32(文字コード) 1 // ラベル1 2B0750FF07 // 機能番号=65287(文字列表示API) 2132B11CF4B11B1 // メモリを1バイト確保する 48D890B130 // Aを確保したメモリへ書き込む 43B019EBFA8411CFE10 // OSECPUのシステムコール命令 CF5B11B1 // メモリを1バイト開放する 9401 // Aに1を加える A10C7F6 // Aと127を比較して等しくなければラベル1へ戻って繰り返し 208A // A=10(文字コード) 2132B11CF4B11B1 // メモリを1バイト確保する 48D890B130 // Aを確保したメモリへ書き込む 43B019EBFA8411CFE10 // OSECPUのシステムコール命令 CF5B11B1 // メモリを1バイト開放する 2B0750FF06 // 機能番号=65286 2B10 // 終了コード=0 43B019EBFA8411CFE10 // OSECPUのシステムコール命令 -なんかちょっと見ただけでもすごく長そうです。はい、なんと86バイトにもなります。これはひどいです。これはもっとも基本的な命令だけを組み合わせて作ったので、どうしても長くなってしまいます。ということで改良です。 --(3) 43B019EBFA8411CFE10 は BFA8 という短縮形に置き換える --(4) 20C20-1-...-9401-A10C7F6 は 60C20C7F...87 という短縮形に置き換える --(5) プログラムの末尾の正常終了処理は全て省略できるので書かないことにする --(6) プログラム最後の改行処理も省略できるので書かないことにする -そうするとこうなります。 05E1 // シグネチャ 60C20C7F // A=32~126までの繰り返し範囲開始 2B0750FF07 // 機能番号=65287(文字列表示API) 2132B11CF4B11B1 // メモリを1バイト確保する 48D890B130 // Aを確保したメモリへ書き込む BFA8 // OSECPUのシステムコール命令 CF5B11B1 // メモリを1バイト開放する 87 // 繰返し範囲終了 -これで31バイトです。かなり短くなりました。まだ8バイトではありませんが。 -ここで説明が必要だと思うので書きます。まず(3)の短縮形はずるいと思われるかもしれません。しかしx86でもCD21というのは実は短縮形で、基本的な命令だけで書いたら10バイト(20桁)以上を要する命令です。ですからこれは「おあいこ」なのです。(4)に相当する短縮形はx86にはありません。私はこれはシステムコール命令以上に頻出な パターンだと思っているのですが、x86の設計者はそうは思わなかったのかもしれません。コードゴルフ的な設計力に関しては私の勝ちですね。(5)はOSの仕様の勝負で、(2)に相当する仕様です。(6)もOSの仕様です。 -さて、このプログラムはまだまだ小さくできます。OSECPUではAPIと頻出する関数呼び出しについて、超短縮形を用意しています。5で始まる命令群です。これはAPIと関数呼び出しのパターンを分析して、パラメータの渡し方をいくつかのタイプに分類して整理して設計したものです。g01もこれとほぼ同じ仕組みを持っています。これを使うとcharsは劇的に短くなります。 --(7) 2B0750FF07-2132B11CF4B11B1-48D890B130-BFA8-CF5B11B1 は 511B00 という短縮形に置き換える 05E1 // シグネチャ 60C20C7F // A=32~126までの繰り返し範囲開始 514B00 // Aの文字を文字列表示APIを使って表示 87 // 繰返し範囲終了 -これで10バイトです。一気に8バイトが見えてきました。さらに以下の省略ルールを使って8バイトにします。 --(8) プログラム末尾の87は省略してよい --(9) プログラムが00で終わる場合、これも省略してよい 05E1 // シグネチャ 60C20C7F // A=32~126までの繰り返し範囲開始 514B // Aの文字を文字列表示APIを使って表示 -ついに8バイトになりました。無駄がなくて美しいです! -(7)の是非はいったん保留にして、(8)と(9)について説明したいと思います。OSECPUでは、プログラムが中途半端な終わり方をしていると判断すると、つじつまを合わせようとする機能を持っています。(8)や(9)はその機能を利用したものです。この手の機能には一部に強い抵抗があることも分かります。なぜなら、プログラマのミスによって中途半端になっているかも知れず、それをエラーも警告もなしに勝手に推測して修復するのはけしからん、バグ発見を困難にする、という主張も可能だからです。 -私はこれに対し次のような見解を持っています。まずソースレベルではこのような省略は許していません。これはまさにその手のミスを防止するためです。しかしそれらのチェックを全てパスした後で、機械語を出力するときにOSECPUが推定修復可能な省略を適用します。プログラマがこれらに気を使わなくてよくなっているのです。もはやバグ発見を困難にする問題がないので、この機能は純粋に便利で賢い機能になっています。 -以上の説明の流れで感じてもらえたと思いますが、コードゴルフは、短く書ける代替表記を探してそれを適用していくことがメインです。つまりコードゴルフに強いOSとは、多くのプログラム中に表れる頻出パターンに対して、短くする方法を十分に用意しておいただけのことだと思っています。OSECPUの場合、頻出パターンを短くするために基本命令はやや長くなる傾向があります(短い書き方を短縮用にとっておいているため)。これをずるいと思う人もいるかもしれません。それも一つの見解です。しかしこれは非常に効果のある手法ですし、私はよい方法だと思っています。 -そして(7)ですが、これも短縮形なのです。それに尽きます。51が文字列表示API呼び出しの短縮形であることを表して、その次の4が短縮形のタイプを表して、B0が変数Aを、そして最後の0が文字列の終端を表しているのです。 *** こだわりのポイント -私のこだわりポイントについて話す前に、まずはコードゴルフに関する状況を紹介したいと思います。 -今のところ、私がライバルだと感じている人は世界に一人しかいません。他にも一生懸命にやっている人がいるのかもしれませんが、私はまだ知りません(すみません!)。これを読んで「なーんだ」と思うかもしれません。「じゃあ二人で低レベルな争いをしているんだな」と思うかもしれません。 -しかしそれはたぶん大きな間違いです。私たちはたった二人ながらも、十分にレベルの高い競争をしてきたのです。私たちはどちらもOSの開発経験があり、独自OSをいくつも作ってきました。そんなプログラマはそんなに多くはありません。ですから腕前はそれなりにあるのです。そしてお互いに何度も追い抜きあっているうちに、ついに世界広しと言えどもこれを越えるものを作れる者は私たち二人以外にはいないだろう、と思えるところにまで来てしまったのです。中二病的だと思われるかもしれませんし、まあ実際そうかもしれないですが(笑)、まあやれるものならやってみてほしいです。 -ということで、完全無差別級のコードゴルフは日本が最高レベルです。しかも圧倒的に最高レベルです。確信しています。これくらいのことを簡単に断言できてしまうくらいに、私たちは研究に研究を重ねてきたのです。・・・もし私たちがどうやってやっているのか知らないのに同じところまでできちゃうすごい人がいたら(それはつまり私たちと同格ということですが)、絶対に友達になりたいです。というか私たちのノウハウを学んでさらに改良してくれる人だって十分にすごいと思うので、やっぱり友達になりたいです。 -きっと誰も賛同してくれないのを分かってて言っていますが(笑)、日本人はコードゴルファーをもっと尊敬してもいいのではないかと思います。だって世界一ですよ。まあくだらない趣味だと言われればそれまでですが、でも世界一ですよ。そう簡単になれるものじゃないんです。日本が世界一になれる分野ってそんなにたくさんあるわけじゃないです。世界一を大事に思ってほしいです。・・・まあ思ってもらえなくても私はがんばりますけどね。 -現在charsに関しては世界記録は私が保持している8バイトです(自称)。しかし、競争相手は11バイトを達成しております(しかも私より先に)。私は今回chars最小記録を奪還したのです。でもその差はたったの3バイトしかないのです。油断はできません。 -先のcharsの鑑賞のところで、私はたった1バイトを削っただけでもかなりの満足感を得ていたことが感じられると思います。それはなぜかと言えば、ライバルに勝つためです。私たちの差は本当に小さいのです。ちょっと油断したら、すぐに追い抜かれてしまうのです。世界一とはそういうものなのです。 -自分でも、何もここまでやらなくても・・・と思うことはよくあります。まあ1バイトくらいいいじゃないかとか思うこともあります。きっと仲良しのライバルだってここまではやらないだろうと思うこともあります。しかしそのたびに思うのです、将来海外の人たちがこの分野に興味を持って、節操なく頑張って世界一を取られたらどうしようと。そしてそのときになって「そのアイデアなら自分も1年以上も前から持っていたんだ、でもやりすぎだと思っていたからやらないんでいたんだ」と言ってみたところで、それは負け犬の遠吠えにしかならないだろうと。 -私は今までずっと競争してきた仲良しのライバルになら負けてもいいんです。彼に負けるのは当然というか、納得できるのです。しかしそれ以外の人に負けるのは到底容認できません。もし負けないようにするチャンスが十分にあったのにそれをしなかったせいで負けたのだとしたら、きっと一生後悔します。だから私はやるのです。たったの1バイトでも取れるものはすべて取るのです。 -そもそもコードゴルフの美学は何ですか。そう、無駄がないことです。削れる部分が残っているようでは美しいなんて言えません。削れると分かっていて削らないという選択はあり得ません。美しくなければコードゴルフしている意味がないからです。 -余談ですが、日本人はコードゴルフに向いているような気がします。なんかこう、小さい物を緻密に作るのは、日本人の得意分野のような気がしませんか? そしてコードゴルフをやると、1バイトの本当の価値が分かるようになります。 -先の例ではどんどん削って短くしていく方向でのコードゴルフをご覧に入れましたが、逆の発想も可能です。まず、絶対に削れないものは何かを考えるのです。helloの場合ですと、"hello, world!"の文字列を削ることはできません。これを削ってしまうと、結局それはhello専用APIを持っているということになるからです。任意の文字列表示をコンパクトにできるものこそ究極なのです(まあhelloやworldなどの基本的な英単語に番号を振ってOSに対して単語番号で表示できるようにするような方法はありだとは思いますが)。 -charsの場合ですと、32と127(20と7F)は削れない情報だと思います。もちろんデータだけではダメです。これらのデータをそのまま表示するのか、それとも開始値や終了値を表しているのかなど、そういうことを示す制御用の情報も必要でしょう。こうして必要なものだけを積み上げていって、究極解に到達することも可能だと思います。私はこの方向で自分のプログラムを解説できるだけの能力がまだないので、この話は今回はここまでです。 -これは当たり前の事実ですが、charsを1バイト小さくするためには、OSに機能を追加しなければなりません。それは当然OSが大きくなってしまうことを意味しますが、その増加量は1バイトではなく、もっとたくさん増えます。つまりOSの大きさとcharsの大きさの合計を見れば、増加以外の何物でもありません。短縮形のサポートにしても、OS側には短縮形と展開形の両方のデータを内部には持っているので、やはりOSのサイズは増えてしまいます。そう考えると、そもそもOS改造も許した無差別コードゴルフって一体なんなのかという気がしてくるかもしれません。 -でもこう考えてみてください。OSの改良によって小さくなるアプリはcharsだけではないのです。他のアプリも小さくなるのです。もしOSが100バイト増加しても、100個以上のアプリが1バイト小さくなるのなら、トータルでは得と言えるではないですか。私たちはまさにそういう観点で考えているのです。実際私はたくさんのアプリを書いてみて、感じをつかみながらOSを改良してきました。ある機能を導入すると、アプリによって大きく なったり小さくなったりまちまちなことがありました。そんなときは、その機能をOSに入れるかどうかで悩むわけですが、その時の基準は、結局トータルではどうなのかということです。将来このOSのアプリ数が1000とかになったとして、その時にどのくらいの割合のアプリがどのくらいの影響を受けそうなのか。それを勘と経験で見積もって取捨選択していくのです。 -とにかく一番やっちゃいけないのは、charsしか小さくならないような機能を入れてしまうことです。それはhello専用APIと同じことです。だからルール違反なのです。というか私の誇りが許しません。特定のアプリしか小さくならない機能なんて全く必要ありません。OSがそれ以上に大きくなってしまうかもしれないとか、そんなのは全部関係なくてそもそもダメなのです。 *** おわりに -さてコードゴルフの世界が少しは分かっていただけたでしょうか。面白いとは思ってもらえないのは分かっています。説明も難解だったと思います。ですからそこは共感してもらえなくていいのです。とにかく私が伝えたかったのは、たかがコードゴルフにここまで熱中している人がいるんだということなのです! ** ほそく -この文章で出てくる「競争相手」はもちろんneriさんのことです! -文中で「私たち」という言葉が何度か出ていますが、それはあるときにはOSASKコミュニティを指し、またあるときは私と競争相手のことを指しているので、文脈によって違います。 * こめんと欄 #comment
テキスト整形のルールを表示する