読者です 読者をやめる 読者になる 読者になる

きくらげ観察日記

好きなことを、適当に。

Quineの書き方

Others

inkar-us-i.hatenablog.com

前回の記事で書き初めと称してQuineを書きましたが、実は僕自身Quineを書くのは初めてでした。
書き始める前はQuineは何をやってるかわからない黒魔術にしか見えませんでしたが、実際に書いてみると意外と楽にできることがわかったので、そのコツのようなものをメモしていきたいと思います。

Quineには、大きく分けて2種類が存在します。

  1. evalを使用しないもの
  2. evalを使用するもの

どちらも微妙な書き方の差はありますが、そこまで大きな違いはありません。難易度的にもそれほど差は無いでしょう。

evalを使用しないQuine

これが最も基本的なQuineであり、どのような言語でもこういったQuineを書くことができます。
evalを使用しないQuineの基本的な書き方は、以下のようなものになります。

x="x=...;(コードの残りの部分);";(コードの残りの部分);

...の部分は適当なプレースホルダにしておき、xの中身そのものは書きません(もしそうしようとした場合、x="x="x=".....とソースコードが無限に入れ子状態になってしまいます)。
(コードの残りの部分)では、xの中身の「...」の部分を実際のxの値(をエスケープしたもの)で置き換え、それを出力します。
すると出力の形が

x="(xの中身)";(コードの残りの部分);

という形になるため、自分自身を出力することができるようになります。

サンプルコード

上に書いていることをそのままJavaScriptにしてみると以下のようになります。

x="x=@?@;console.log(x.split(String.fromCharCode(64)).join(String.fromCharCode(34)).replace(String.fromCharCode(63), x))";console.log(x.split(String.fromCharCode(64)).join(String.fromCharCode(34)).replace(String.fromCharCode(63), x))

String.fromCharCode(34)は'"'で、String.fromCharCode(64)は'@'です。
このコードでは...の部分を@?@と書き、後で@を"、?をxの中身に置き換えています。

ちなみに、xの値は文字列でなくてもかまいません。例えばLisp系の言語なら、xの値をS式にしたほうが楽だったりします。

(let1 x '(let1 x ? (write (cons (car x) (cons (cadr x) (cons (list 'quote x) (cdddr x))))) (newline)) (write (cons (car x) (cons (cadr x) (cons (list 'quote x) (cdddr x))))) (newline))

evalを使用するQuine

JavaScriptRubyなどのevalがある言語では、evalを用いたQuineを書くことができます。基本的な書き方は以下のようになります。

x="(任意のコード)";eval(x);

最初にx="..."でコード本体を代入しているため、eval(x);の実行中はxという名前でコード本体にアクセスすることができるようになります。そのため、"(任意のコード)"の中では

"x=\"(xをエスケープした値)\";eval(x);"

という文字列を作って、それを出力してしまえば完成です。こちらの方法は前者に比べて比較的簡単に書くことができ、同じコードを2回書かなくて良くなるためコード量も短くなります。また、コードの大半が文字列になるため、コード自体を整形してAAにするといったようなことが簡単にできます。

サンプルコード

x="q=String.fromCharCode(34);console.log('x='+q+x+q+';eval(x);');";eval(x);

String.fromCharCode(34)は'"'です。

もちろんこちらも文字列ではなくS式で書いたりすることもできます。

(define x '(begin (write `(define x ,(list 'quote x))) (write '(eval x (interaction-environment))) (newline)))(eval x (interaction-environment))

これで基本的なQuineの書き方は理解いただけたかと思います。
世の中には100言語Quineリレーのような意味のわからないものもありますが、それらも全て方法としては今書いたことと全く同じことを行っています。上に書いた方法さえあればあとは根気だけでなんとかなるはずなので、正月の暇なときにでもぜひ皆さんQuineに挑戦してみてください。