Perl 記号ゴルフと Acme::EyeDrops

ここからが本題です。 Perl は頭がおかしいです。すごすぎる。この感動を伝えたいと思います。まず Perl の基本からです。 Hello world! を書きましょう。

''=~('(?{'.('[[).[|`%,,/`[/[@$'^'+)@@/^(@@@@@,@),@').'!
"})')

えーと。このくらいわかりますよね。これは Hello world という一番簡単なプログラムなのでわからない人はちょっとプログラムの才能が無いです。嘘ですが。

Perl では記号だけでは出力もできないかな、というような話をしていたところ、 Acme::EyeDrops の出力は記号だけで出力してるぞ、ということでした。これは見たことがあったのですが、確かに今見ると記号だけです。で、これの出力の意味を理解してたんですが、なんともすごいものでした。書いてから後で Perl ヤロウどものことだから既に常識だったらどうしよう、と思ってぐぐってみたんですが、日本語ではたぶん Acme::EyeDrops の内容の解説ぽいのは無いように思いました。

拡張正規表現で eval

まず出力をどうやってるかからいきますか。まぁ eval です。 eval 自体は記号じゃないですし、 s///e なんかも、記号じゃないのが混じってしまいます。しかしそこは PerlRuby には無いものをもっています。そこにシビれます。用途不明気味な拡張正規表現 (?{}) があります。

''=~/(?{print"Hello world!\n"})/;

これで eval が実現できます。

暗黙型変換

上のヤツは、正規表現も文字列も適当にごっちゃになるので、

''=~'(?{print"Hello world!\n"})';

でもいいです。

コンパイル時の文字列リテラル結合

さてとなると問題はこの中身である print... っていう文字列を作るだけです。簡単だと思ったら、簡単じゃないです。

$_='(?{print"Hello world!\n"})';
''=~$_;

がうまく実行できません。

Eval-group not allowed at runtime, use re 'eval' in regex m/(?{print"Hello world!\n"})/ at test.pl line 2.

とのことです。これでは 'H' などを記号から合成することができません。しかしそこはやはり Perl 、ちゃんと回避できます。どうやら 'ho'.'ge' というような定数リテラルの計算はコンパイル時にやってくれるみたいで、

''=~('(?{print"Hello'.' world!\n"})');

などとすれば、ちゃんと処理してくれます。外側に () が増えましたが、これは . の優先順位が =~ より低いことから来ています。実はこれが最後の謎で、 ('') っていう顔文字記法とかあるのか…とか思ってたんですが。

文字列に論理演算

さて、ここまで来れば "H" とかを作るだけです。しかしここでも問題があり
ます。 Ruby だと適当に文字リテラル由来の数値を足し引きして適当に作っ
たのです。例えば "H" が欲しければ

''<<?$+?$

です。しかし Perl にはたぶん記号だけで数値を文字にする方法がありません(普通は chr 関数を使う)。さらに普通に足し算をしてみると、

i@u ~/wrk/golf> perl -e 'print "\$"+"\$","\n"'
0

はい勝手に数値になるので困ったものです。しかしそこは Perl 、論理演算だけは「正常」に行えます。

i@u ~/wrk/golf> perl -e 'print "`"^"(","\n"'
H

記号しか打てないキーボードで作業する際など、 ` と ( の xor を取って H を作るという知識は日常業務でも使えそうですね。有用なティップスです。これでまぁ、 eval ができるので Perl は記号だけでも Turing Complete ですね。ppencode で記号が無しプログラミングも強いですしPerl は最強言語ですね 良かった良かった。おいしいのは

i@u ~/wrk/golf> perl -e 'print "`%"^"(@","\n"'
He

などと2文字一気に作れることです。おかげで最初に上げた Hello world! は 0H61B で Ruby より短いです。たぶんこれ Brainf*ck よりも短いですよね。最強です。あ、ちなみに最初のヤツの '(?{' とかも xor に叩き込んでも同じバイト数で、 0H61B でした。

''=~('"`_[[).[|`%,,/`[/[@$~)^##'^'
_$+)@@/^(@@@@@,@),@_#|^
')

これ以上短くなるかは知りません。あ、改行も極めて重要なのでコピペする場合はその情報落とさないように。ちょっと失敗すると

i@u ~/wrk/golf> perl test.pl
panic: top_env

という極めて informative なエラーメッセージが頂けますので。

ここまで4つの要素について考えてきたのですが、少し考えてみると、どれもこれもさっぱりいりません(文字列結合だけは速度の面からあると嬉しくはあるのかも)。一体全体どうしたもんかなと。 Perl すごいなと。ところで、

s/???/???/eeeeeeeeeeeeeeee

と無限に eeee を増やしていって、 e の数で結果が変わるような ??? はあるだろうか、とふと思ったのですが。まぁ宿題です。

ちょっと考えると、この ??? は quine 的なものですね。んじゃそこらじゅうにありそうです。悪即斬とか。

なにかあれば下記メールアドレスへ。
shinichiro.hamaji _at_ gmail.com
shinichiro.h