|_|=
記号ゴルフって英語でなんやろって聞いたら題字の返事が mark byers 氏から帰ってきました。
さて、最近の記号ゴルフについては、
http://d.hatena.ne.jp/kikx/20061030#1162308720
にまとまっていますが、とりあえず無限ループは [*""..$/] と (""..$/).min で更新できますねーと。
あとは quine も StackError も SEGV もわからないなぁといったところです。特に quine を「安直に」作る kik さんは異常だと思いました。つまり答えキボンヌと申しておるのですが。
追記なんかこれ偉そうですね。こう答えがわからないギギギというアレが。 StackError は正規表現とかなんかなぁ…あ、あと思い出したんですが 6B StackError は察しておられる通り load$0 のつもりでした。 Perl Golf では do$0 がたまにあるワザのようなので(再帰関数をこれで作る)、まぁアリなのかなと思ってました。
追記: 教えていただきました!
http://d.hatena.ne.jp/kikx/20061107#1162843513
ありがとうございますー。ていうかすげー。 StackError の方はちょっと考えてやっとわかりました。 quine の方はそもそも quine については何度か考えてみたことあるんですがわかった試しが無いので、この機会にもうちょい考えて私も安直に quine が日常業務で書けるように修行を積みたいと思います…と思って眺めてたら、動き自体はわかりました。これ自分で書ける気がしないなぁ… Array#* が %p の代用みたいな感覚なんですね気持ち的には。なるほどなぁ…
あ、忘れてましたが、たぶん「p」と当てられた通り、猿マネしただけの quine 1H25B は以下のようなものでした。
_="_=%p;$><<_%%_";$><<_%_
とか書いてなんとか縮まんかと考えてるうちに改行アリの場合のが。これは Wikipedia で似たの見ました(何故イタ語)が、ほえーというかなんというか。
わかってきた…細かいミスだと思いますが、
i@u ~/wrk/golf> ruby =(ruby =(ruby =(ruby quine.rb))) $><<[_=<<_,_,'_'] $><<[_=<<_,_,'_'] _
でもいいですよね。 0H37B かと。しかし本質的にはカケラのムダも見つからないなぁ…
追記。とてもアホなミスで見落としてたIRCのログがあったのですが、そこで flagitious さんがもう 2B 短い回答を提示してました。それは少し内容は違うんですが、その方法を使うと kik さんの quine もまだ 2B 減ります…
Perl 記号ゴルフと Acme::EyeDrops
ここからが本題です。 Perl は頭がおかしいです。すごすぎる。この感動を伝えたいと思います。まず Perl の基本からです。 Hello world! を書きましょう。
''=~('(?{'.('[[).[|`%,,/`[/[@$'^'+)@@/^(@@@@@,@),@').'! "})')
えーと。このくらいわかりますよね。これは Hello world という一番簡単なプログラムなのでわからない人はちょっとプログラムの才能が無いです。嘘ですが。
Perl では記号だけでは出力もできないかな、というような話をしていたところ、 Acme::EyeDrops の出力は記号だけで出力してるぞ、ということでした。これは見たことがあったのですが、確かに今見ると記号だけです。で、これの出力の意味を理解してたんですが、なんともすごいものでした。書いてから後で Perl ヤロウどものことだから既に常識だったらどうしよう、と思ってぐぐってみたんですが、日本語ではたぶん Acme::EyeDrops の内容の解説ぽいのは無いように思いました。
拡張正規表現で eval
まず出力をどうやってるかからいきますか。まぁ eval です。 eval 自体は記号じゃないですし、 s///e なんかも、記号じゃないのが混じってしまいます。しかしそこは Perl 、 Ruby には無いものをもっています。そこにシビれます。用途不明気味な拡張正規表現 (?{}) があります。
''=~/(?{print"Hello world!\n"})/;
これで eval が実現できます。
コンパイル時の文字列リテラル結合
さてとなると問題はこの中身である 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 的なものですね。んじゃそこらじゅうにありそうです。悪即斬とか。