円周率 π = 3.14159265... を語呂合わせで 「産医師異国に向こう…」 として覚えるみたいなのありますよね。 英語では Piphilology と言って、たとえば、こんなの。
How I wish I could recollect pi easily today...
長さ 3 の単語 (How)、長さ 1 の単語 (I)、長さ 4 の単語 (wish)、… という列で数字の列を覚えたりするそうです。 この方式だとゼロの桁の表現に困りそうですが、ゼロの所には巧みに句読点などの記号を配したり、 あるいはもう少し単純に、長さ 10 の単語を当てて表すのだそうな。
と、いうわけで。
長さ 3 のトークン、長さ 1 のトークン、長さ 4 のトークン、 長さ 5 のトークン、長さ 9 のトークン、長さ 2 のトークン、 長さ 6 のトークン、…という列でできている Ruby プログラムを書いてみました。 (ソースコードの全体はこちら)
$ cat entry.rb
big, temp = Array 100000000**0x04e2
srand big
alias $curTerm $initTerm
(中略)
print pi
このプログラムを暗記すれば、トークンの長さで円周率 242 桁分まで覚えられます。 Ruby 付属の Ripper ライブラリでRubyの字句解析を実行して長さを数えると、こんな感じ。
$ ruby -r ripper -e \
'puts Ripper.tokenize(STDIN).grep(/\S/).map{|t|t.size%10}.join' < entry.rb
31415926535897932384626433832795028841971693993751058209749445923078164062862089
98628034825342117067982148086513282306647093844609550582231725359408128481117450
28410270193852110555964462294895493038196442881097566593344612847564823378678316
52
ちなみに、プログラムを暗記するのが苦手な人は、単純に実行して下さい。
$ ruby entry.rb
31415926535897932384626433832795028841971693993751058209749445923078164062862...
1万桁まで計算して表示してくれます。
と、まあ、こんなプログラムを、意味のわからないRubyプログラムを競うコンテスト TRICK 2015 に投稿してました。 2年前の "いろは歌 in Ruby" に続いての参戦になります。なんか連覇できたみたいです。ありがとうございます。
やってみるとわかるんですが、円周率に沿ったトークン長で意図したプログラムを書くというの、 意外と難しいんですよ、これ。
いや、正確には、意図したプログラムを書く「だけ」なら、少なくとも Ruby では難しくないです。
円周率の中にたまたま狙いにあった数列が現れるまで、要らない桁をひたすら捨て続ければいい。
円周率の中に 123456
という部分があったとして、その部分を捨てたければ
[改行]1[改行]22[改行]333[改行]4444[改行]55555[改行]666666[改行]
とかすれば副作用無しで捨てられる。
ただ、これをやっていると途方もない長さのプログラムになってしまいます。
例えば x = 1024
という文を書きたかったとして、このトークン長にあった
114
が円周率の中に現れるまで捨て続けるとすると、
大雑把にいって1000桁に1度の割合でしか出てこないので、
この文を一つ書くだけで1000トークン捨てないといけないわけです。
この時点で 4096Byte 以内という TRICK の投稿条件をオーバーしてます。
何とかしてコードゴルフ力を駆使して、
円周率の形に合わせつつ短いプログラムを書く必要があります。
print pi
で終われるのが何か格好いいと思ったからです。
理由その2は、242トークンのうち捨てトークンでない、
ちゃんと計算の一部になってるのが77トークンなんですけど、242/77 = 22/7 ≒ 3.14
って古代から伝わる円周率の第一近似なので格好いいと思ったからです。
本当は75/242トークンで書けているのでちょっと水増ししました。
alias $aa $bcd
という構文でグローバル変数のエイリアスできるというのがあって、
これは便利でした。
srand
という大変便利な関数が Ruby にはあって、
これは前回渡した引数 (seed) を次回の呼び出しの時に返すという動作をする関数です。
変数への代入以外の方法で値を覚えておけるというのは、今回の制約ではとても貴重です。
今回のプログラムを書く前は
rand
などの乱数に関係ある関数として使っていた記憶がありますが、
私の誤解だったようです。
def
や end
なので意外な長距離を結ぶ制御が可能になることがあります。遠距離砲です。
今回提出した最終版ではあまり積極的には使っていませんが、
途中で試行錯誤する過程で面白く使えました。
という感じで、一日円周率を睨んでいると、ただの数字が本当に ↑↑↑ こんな風な性格を持ったプログラム片に見えてくるようになりました。 おかげで少しは円周率に親しくなれたかなぁという気がします。