マーケターからエンジニアに転向した堀といいます。
今までいろいろ勉強してきましたが、良い機会なので初心に立ち返りたいと思います。
初心と言えばHello World
という事で、最初に習ったhello worldを今できる方法で新しく実装したい!
さらに、RubyKaigi2015 TRICKが最高だった!
ということで、quineでhello worldしたいと思います。
結果
作り方
quineとは、上画像のようにソースコードと出力結果の文字列が一致するプログラムのことです。
rubyで言うと、diff <(ruby quine.rb) <(cat quine.rb)を満たすようなコードのことです。
上述のTRICKにも出ておられた、遠藤侑介さんの「あなたの知らない超絶技巧プログラミングの世界」を参考に作っていきます。
http://www.amazon.co.jp/dp/4774176435
まず画像を表現するテキストを用意
コードの形
eval$s=%w(
s = %(eval$s=%w(#{$s})*""); #sにはこのソースコード全体の文字列がバインドされます。
f = -> n { s.slice!(0, n) }; #ソースからn文字ずつ取り出す処理。
#sを一行ずつ整形して表示するコードを書いていく
puts(s)
)*""
このコードは、「あなたの知らない超絶技巧プログラミングの世界」遠藤侑介 p.137から引用してコメントをつけたものです。一つずつ見てましょう。
eval$s=%w( hoge hoge )*""
Array#*
はjoinと同じ動きをするので、%w( hoge hoge )*""
は全体で、'hogehoge'を返します。
こうする事で、好きな場所に空白を入れても処理時に全て無視されるようになります。
ソースコードを画像にそって整形していく
空白部分は、そのまま空白を入れても反映されないので、32.chr * nで表現します。
例えば4行目は、
puts(f[2] + 32.chr * 4 +f[3] + 32.chr * 3 +f[3] + 32.chr * 9 +f[3] + 32.chr * 4 +f[7] + 32.chr * 4 +f[9] + 32.chr * 8 +f[10] + 32.chr * 2 +f[5] + 32.chr * 2 +f[4] + 32.chr * 2 +f[5] + 32.chr * 8 +f[5] + 32.chr * 7 +f[3] + 32.chr * 4 +f[6] + 32.chr * 7 +f[3]);
となります。
これは自動で変換するコードを書きましょう
いろいろと調整
字数と余白のバランスを見ながらコメント入れたりして整形します。
最終的にはこうなりました。
eval$s=%w(
s = %(eval$s=%w(#{$s})*"");
f = -> n { s.slice!(0, n) };
puts(f[132]); #1行目。全部文字
puts(f[132]); #2行目。以下同様
puts(f[132]);
puts(f[2] + 32.chr * 4 +f[3] + 32.chr * 3 +f[3] + 32.chr * 9 +f[3] + 32.chr * 4 +f[7] + 32.chr * 4 +f[9] + 32.chr * 8 +f[10] + 32.chr * 2 +f[5] + 32.chr * 2 +f[4] + 32.chr * 2 +f[5] + 32.chr * 8 +f[5] + 32.chr * 7 +f[3] + 32.chr * 4 +f[6] + 32.chr * 7 +f[3]);
puts(f[2] + 32.chr * 4 +f[3] + 32.chr * 3 +f[3] + 32.chr * 4 +f[8] + 32.chr * 4 +f[7] + 32.chr * 4 +f[7] + 32.chr * 3 +f[6] + 32.chr * 3 +f[8] + 32.chr * 2 +f[3] + 32.chr * 2 +f[1] + 32.chr * 2 +f[3] + 32.chr * 2 +f[3] + 32.chr * 3 +f[6] + 32.chr * 3 +f[3] + 32.chr * 2 +f[4] + 32.chr * 2 +f[2] + 32.chr * 4 +f[6] + 32.chr * 2 +f[3] + 32.chr * 2 +f[3]);
puts(f[2] + 32.chr * 10 +f[3] + 32.chr * 8 +f[4] + 32.chr * 4 +f[7] + 32.chr * 4 +f[7] + 32.chr * 3 +f[6] + 32.chr * 3 +f[8] + 32.chr * 2 +f[3] + 32.chr * 2 +f[1] + 32.chr * 2 +f[3] + 32.chr * 2 +f[3] + 32.chr * 3 +f[6] + 32.chr * 3 +f[3] + 32.chr * 7 +f[3] + 32.chr * 4 +f[6] + 32.chr * 2 +f[4] + 32.chr * 1 +f[3]);
puts(f[2] + 32.chr * 4 +f[3] + 32.chr * 3 +f[3] + 32.chr * 4 +f[8] + 32.chr * 4 +f[7] + 32.chr * 4 +f[7] + 32.chr * 3 +f[6] + 32.chr * 3 +f[9] + 32.chr * 2 +f[1] + 32.chr * 2 +f[3] + 32.chr * 2 +f[1] + 32.chr * 2 +f[4] + 32.chr * 3 +f[6] + 32.chr * 3 +f[3] + 32.chr * 4 +f[6] + 32.chr * 4 +f[6] + 32.chr * 2 +f[3] + 32.chr * 2 +f[3]);
puts(f[2] + 32.chr * 4 +f[3] + 32.chr * 3 +f[3] + 32.chr * 9 +f[3] + 32.chr * 8 +f[3] + 32.chr * 7 +f[5] + 32.chr * 8 +f[12] + 32.chr * 2 +f[6] + 32.chr * 2 +f[8] + 32.chr * 8 +f[6] + 32.chr * 2 +f[2] + 32.chr * 4 +f[2] + 32.chr * 7 +f[3] + 32.chr * 7 +f[3]);
puts(f[132]);
puts(f[132]);
puts(f[132]);
puts(s);
#AAA
;;
)*""
おまけ quineの基本
quineとは、ソースコードと出力結果の文字列が一致するようなプログラムを指します。
具体的には、diff <(ruby quine.rb) <(cat quine.rb)
を満たすなら、このrubyコードはquineだと言えます。
自己再帰的なコードはどうやったら作れる??
コードには、表示されるデータ部分(D)と表示などの処理をする部分(P)が必要です。
普通に処理するとDしか表示されませんが、quineにするには両方を表示させないといけません。
そのための方法には
- Dの内容を処理時に変換して、Pを含む形にする
- 最初からDとPを一致させる。つまりevalを使う
の2つがあります。
処理時にPをDに含ませる
データ部分に、処理文字列が一切含まれていなところから追加するのは大変なので、
'D + P' + P #-> 'D + P + P'という形を目指します。
D2 = 'D + P'とおくと、処理PはD2の'D'をD2で置換すれば良いわけです。
d2 = "d; print s.sub('d', d2)"; print d2.sub('d', d2)
この結果は、
d; print s.sub('d', d2); print s.sub('d', d2)
となり殆ど一致しています。
整えると、超絶技巧プログラミングの本に出てくる形
s = "s = ...; print s.sub(\"...\", s)"; print s.sub("...", s.dump)
になります。
evalを使う
evalは文字列自体に処理を任せられるので、
eval s='puts %(#{s})' #=> puts %(#{s})
整えて、
eval s=%q(puts %(eval s=%q(#{s})))
となります。これも超絶技巧プログラミングの本にのっています。
おわりに
いかがでしょうか??
実務の役に立たないこと間違いなしですが、プログラミング愛は深まります。
ぜひこの本を買って、RubyKaigi2016のTRICKに一緒に応募しましょう!