Derive Your Dreams

about me
Twitter: @kinaba

22:41 13/06/01

TRICK 2013 @ RubyKaigi

いろは歌。


in Ruby.

!@THEqQUICKbBROWNfFXjJMPSvVLAZYDGgkyz&[%r{\"}mosx,4>6]|?'while(putc 3_0-~$.+=9/2^5;)<18*7and:`#

ということで、ASCII の制御文字じゃない部分、' ' (0x20) から '~' (0x7E) まで95文字をちょうど一回ずつ使って、 その95文字をちょうど一回ずつ標準出力に書くプログラムです。Ruby会議に合わせて開かれた TRICK 2013 という、プログラムの"お前はなにをやっているんだ度"を競うコンテストに出してみたら優勝してしまいました。 やった!

簡単な解説

「各文字を1回しか使わない」 という制約が真っ先に効いてくるのは、 これすなわち、 1個の変数を1度しか使えない、 というところです。 C言語のfor文などに見えるように、 変数というのは宣言/初期化して更新して検査して値を使う、 と4回くらいコード中に現れるのが普通です。 もっと高級なパラダイムやライブラリに頼れば2回にはなりますが、 1回というのは、流石に、その、変数という概念の存在意義を問われる事態です。 ちなみに Ruby での "高級" なやりかただと、 色々なループ処理を Enumerable や Integer へのブロック渡しで処理するものですが、

32.upto(125){|c| putc c}

いきなり | を2回使われてしまって泣きそうです。というわけで、

というあたりで、「各文字を1回しか使わない」 を解決しました。 細かい所ですが +=131+ で2回 + の文字を使うのを避けるために -~ というマイナーな二項演算子を使っていまして、 x-~yx+y+1 と同じ意味です。 嘘です。 ビット反転 ~ してからの引き算ですね。

実は大変なのは、「他の全ての文字を1回使う」 という義務の方です。 まあ全部コメントか文字列リテラルに突っ込めば全部使えるのですが、 それは面白くないので、できるだけ面白く文字を捨てるように心がけました。

気をつけたのは、そんなところです。

挑戦状

賞を頂いておきながらなんですが、実は、自分としては、このコード、まだまだ不満です。 なぜって

!@THEqQUICKbBROWNfFXjJMPSvVLAZYDGgkyz&[%r{\"}mosx,4>6]|?'while(putc 3_0-~$.+=9/2^5;)<18*7and:`#

プログラムとして 意味のある 部分が全体の3分の1しかない。 他は頑張って意味のないことをさせて、 文字をひたすら捨てています。 95文字すべてとはいいませんが、 できるだけ多くの文字がプログラムの働きに貢献する、 そんな 「Ruby いろは歌」 はないものでしょうか。 ぜひ、これを見た皆様に挑戦していただきたいと思っています。

自分でも挑戦したのですが、うまく行きませんでした。 実のところ、最初の予定では、まったく文字が重ならない二つのプログラムを二つ並べて、 片方は stdout に、 片方は stderr に 95 文字を吐くものを書く予定でした。 …そのくらいやれば60文字くらい使うだろうし、 $. を使うトリックはすぐにできたので、 もう一個書くくらいは簡単だと思っていたのです。 …が、while putc を避けてもう一つ同じ処理を書くのは……少なくとも締め切り当日の1日では無理でした。 無念。

たぶん、逆方向からアプローチした方が面白いんではないかと思っています。 つまり、Ruby の標準ライブラリと言語仕様をサーチして、 できるだけ英大文字と小文字を消費できるメソッドの組み合わせを探索する。 そこで得られたメソッドの組み合わせから、落語の三題噺のように、 面白い動作をするプログラムを編み出す。 機会があればやってみたいと思います。

というわけで最後にもう一度、 できるだけ多くの文字がプログラムの働きに貢献する、 そんな 「Ruby いろは歌」 はないものでしょうか。 ぜひ、これを見た皆様に挑戦していただきたいと思っています。

presented by k.inaba (kiki .a.t. kmonos.net) under CC0