著作一覧 |
code jam japanからGoogle Code Jam Japan 2011 T-shirtに行って、そこでQuineの作り方を読んでちょっと考えた。
というのは、それまでえんどうさんがなんかすごいのやってるなぁとかRuby会議とかで見てはいたが全然興味が持てなかったのだが、「ここではそういった一般的な書き方の説明は省略させていただいて」とあるので突然、そういった一般的な書き方とはなんぞや? とひっかかったからだ。
で、なんとなくQuineで検索したらen.wikipediaのQuineの項が引っ掛かったので、そこにあるサンプル(Java)を眺める。(今、検索をすると日本語のWikipediaが先に引っ掛かるが、元の環境ではenが先に引っ掛かるのであった)
すると、1. パート3で利用する小物を定義、2. 出力用に全体を記述、3. 出力を記述。出力の制御にパート1で用意した小物を利用、という構成だなとわかった。
で、Rubyにはいろんな書き方があるとか書いてったので、ヒアドキュメントなら"が不要な分、楽だろうと書き始めてみる。
x=<<D
xに代入して、あとでそれを出力すれば良いだろうと考えた。このxがパート3で利用する小物だ。
当然、それを出力するのだから、
x=<<D x=<<D D puts x
まではすぐに書ける。
ということは、puts xも文字列に入れる必要があるから、
x=<<D x=<<D puts x D puts x
となる。で、実行してみると、当然だが
x=<<D puts x
が得られる。この最初の行を2回、間にDを挟んで2行目を2回出力するのだから、
x=<<D x=<<D 1.upto(2){puts x} D 1.upto(2){puts x}
とすると当たり前だが1行ずつではなく2行ずつの繰り返しになるわけで、ということは行あたりに2回ずつputsするのだから
y=10.chr;x=<<D y=10.chr;x=<<D 1.upto(2){puts x.split(y)[0]};1.upto(2){puts x.split(y)[1]} D 1.upto(2){puts x.split(y)[0]};1.upto(2){puts x.split(y)[1]}
(で、10.chrを思いつくまで"\n"(と書くと途中でヒアドキュメントが切れるのでうまくいかない)をどうやって出力行の中に入れるのかでちょっと悩んだ)として、パート1にパート3で文字列を区切るのに利用する新たな変数yを追加してやれば
y=10.chr;x=<<D y=10.chr;x=<<D 1.upto(2){puts x.split(y)[0]};1.upto(2){puts x.split(y)[1]} 1.upto(2){puts x.split(y)[0]};1.upto(2){puts x.split(y)[1]}
が出力されるから、後はヒアドキュメントのエンドセンチネルに使っているDをどうにかすれば良い。最後の行に単独で置くことになるから評価してエラーにならず出力もされず、かつその値がDと出力されれば良いのだから、:Dか'D'のどちらかだろうということはわかる。で、putsに,で区切った引数を与えればそれぞれが改行されて出力されるのだから、2つのputsの後ろにつけてやれば良いはずなので、同じくパート1でDを宣言というか定義、パート3で利用して
D=:D;y=10.chr;x=<<D D=:D;y=10.chr;x=<<D 1.upto(2){puts x.split(y)[0]};1.upto(2){puts x.split(y)[1],D} D 1.upto(2){puts x.split(y)[0]};1.upto(2){puts x.split(y)[1],D} D
とすれば良いはず。で、試してみると
D=:D;y=10.chr;x=<<D D=:D;y=10.chr;x=<<D 1.upto(2){puts x.split(y)[0]};1.upto(2){puts x.split(y)[1],D} D 1.upto(2){puts x.split(y)[0]};1.upto(2){puts x.split(y)[1],D} D
同じものが出力された。なるほど一般的な書き方というのはこういう感じなのかなと思うのだが、どんなものだろうか。
追記:ちょっとリファクタリング
a=[];D=:D;y=10.chr;x=<<D a=[];D=:D;y=10.chr;x=<<D 0.upto(1){|n|1.upto(2){a<<x.split(y)[n]}};puts a.insert(3,D) D 0.upto(1){|n|1.upto(2){a<<x.split(y)[n]}};puts a.insert(3,D)
追記:さらにリファクタリング
D=:D;y=10.chr;x=<<D D=:D;y=10.chr;x=<<D puts x.split(y).zip(x.split(y)).flatten.insert(3,D) D puts x.split(y).zip(x.split(y)).flatten.insert(3,D)
ジェズイットを見習え |
はい、だいたいそういう形のものを一般的な感じだと考えています。 ksk さんの書かれた文章が僕の中ではわかりやすいです。<br><br>http://d.hatena.ne.jp/KeisukeNakano/20070814/1187070401<br><br>ken thompson のこの論文の FIGURE 1 なんかが僕の思い描く一般的な形です。<br><br>http://cm.bell-labs.com/who/ken/trust.html<br><br>こいう感じで書けるなら brainfuck でも時間さえかければ書けそうだな…という感じていただければ、それがたぶん一般的な書き方と僕が呼んでるものです。
おお、そのkskさんのはわかりやすいですね。どうもありがとうございます。<br>Ken Tompsonのは、なんでforで回しているところが%dなんだろう?
自己フォロー:%dについては「これそのものは違うけど生成したやつがQnineになっているから勘弁な」と書いてあった。