Quine

date:2013/03/19

1. Quine とは

最近 Quine という遊びを知ったので、やってみました。
ただ、簡単なものしかやってないので、すでに Quine を知っている人にとっては既知のコードしか出てこないと思います。(本当は Template Haskell 使って、Quine を考える予定だったのだけれど、Template Haskell をまだ良くわかってない。)
Quine とは、いったいなんでしょうか?
“自分自身を出力するプログラム” であって、入力を伴わないようなものです。
この性質は、”プログラムを吐くプログラム”に対して、”プログラムを実行する”、という関数が考えられますが、その不動点が Quine である、という様に理解できると思います。
そのため、理論的にも興味のある対象だと思います。
私が Quine を知ったのは、
Quine・難解プログラミングについて というスライドがきっかけです。
このスライドを書いてる方は、非常に<del>ヤバイ</del>スゴイ人で、他にも
などといった、遊び心あふれるQuineを書いています。
以下では、簡単な Quine を Haskell で書いてみたいと思います。
ちなみに、Ruby での簡単な Quine として、
eval s="puts 'eval s=' + s.inspect"
があります。
というわけで、Ruby にも非自明な Quine が存在しました。
(ここでは、自明な Quine とは、空のプログラムのことだとします。)

2. Haskell での例

Haskell での例を作ってみます。
とにかく必要なこととして、最終的には自分自身を出力する部分があります。
main = putStr("...")
という部分は必要でしょう。さらに、今書いた部分も表示する必要があるので、
main = putStr("main = putStr(\"...\")")
という形で表されるはずです。 しかし、これを続けようとしても、
main = putStr("main = putStr(\"main = putStr(...)\")")
となって、これを続けていくにしても、長さ無限のプログラムが必要になってしまいます。
さて、どうしましょうか。

2.1. 変数を使う

上に書いたものについて、同じ部分を変数 var に置き換えておきます。
var = "main = putStr$..(1)..";main=putStr$..(2)..
ここで、このプログラムを実行すると..(2)..が出力されるわけですが、
var= ”...” という形で始まる必要があるので、
var = "main = putStr$..(1)..";main=putStr$"var = " ++ show var ..(2')..
という形に書けるはずです。
ここで、 “show var” という部分は、 var の内容を文字列に変換するものです。
つまりこれを実行すると、
var = "main = putStr$..(1)....(2').."
となります。
元々、変数を使った理由は後半に、putStr がネストしていたからで、それを踏まえて..(2’)..が ;main=... と表すようにしておきます。つまり、..(2’).. を ++ ”;”++var に置き換えます。
var = "main = putStr$..(1)..";main=putStr$"var = " ++ show var ++ ";" ++ var
-- => var = "main = putStr$..(1)..;main=putStr$..(1).."
ここまで来ると、もう..(1)..の形も決まっています。
ただし、”“の中で”を使う必要があるので、”とエスケープします。
というわけで、 (1) を “var = ” ++ show var ++ ”;” ++ var” と置き換えます。
var = "main = putStr$\"var = \" ++ show var ++ \";\" ++ var";main = putStr$"var = " ++ show var ++ ";" ++ var
これで Quine になりました!!!!!
割と思いつくのに時間がかかったのですが、ちゃんとステップごとにやればどうにかなる気がします。

2.2. 関数でどうこうする (ちょっと不完全)

もう1つ例を出します。
発想を全く変えて、1つの引数を受け取ったらそれを複製する関数を書きます。
\(\lambda\) 計算を知っている人には自然なのかもしれません。
\((\lambda x.xx)(\lambda x.xx)\) という項の類似のつもりで書きました。(私は \(\lambda\) 計算に詳しくないのでよく知りません。もしかしたら型付き \(\lambda\) 計算と呼ばれるやつには、もっとそれっぽいのがあるのかも知れません。)
(\x -> putStr(x++" "++show x)) "(\\x -> putStr(x++\" \"++show x)"
このプログラムは、main がないので、正しくコンパイルして実行することはできませんが、
インタプリタ上で実行することはできます。(たとえば ghci)
コンパイルできる形で書こうと思ったのですが、イマイチ良い書き方が思いつきませんでした。

3. 終わり

というわけで、遊びで Quine を書きました。
もっと複雑なことができれば楽しそうですね。