目次
背景
こんにちは。 かりんとうマニア(@karintozuki)です。
私が初めてQuineについてのWikipedia記事を読んだとき10分くらい悩んだ末、何もわからん!と諦めた記憶があります。今思えば、当時はJavaでプログラミングをしていて、JavaはボイラープレートがあるせいでQuineに向いてない言語だったのが原因かもしれません。
最近、改めてQuineを勉強したところ、何がどうなっているのか腹落ちした感じがしました。なのでこの記事では誰でもわかるようにシンプルにQuineをゼロから作る過程を紹介しようと思います。
注: 記事の校正にAIを利用しましたが、コードはすべて自作です。
Quineの構造を理解する
ここでは、構文が簡潔で人気のあるPythonを使って説明します。この記事の原理を理解すれば、大体どの言語でもQuineが書けるようになるはずです。
まず、今回作成するPythonのQuineがこちらです。
1 | s='s=%c%s%c; print(s%%(39,s,39)'; print(s%(39,s,39)) |
実際に動かしたい場合は、https://pythononline.net/ のようなオンラインインタープリタが便利です。
このコードが今理解できていなくても全然問題ありません。順を追って説明していきます。
Step 1: データと出力
さて、最初に以下のようなコードを考えてみます。
1 | # Ver.1 |
このコードはシンプルに文字列print(s)を出力するだけですが、Quineに必要な2つの要素を含んでいます。
- データ定義 — プログラムのコードをデータとして保持する
- データ出力 — そのデータを画面に出力する
1 | s="print(s)" # データ定義 |
データ定義のパラドックス
先程のVer 1では、データ定義と出力のうちデータ出力部分を出力していました。完全なQuineにするには、データ定義の部分も出力する必要があります。そこで、コードを次のように編集しました。
1 | # Ver. 2 |
(文字列に改行を含めるため、Pythonの"""を使用したことに注意してください。)
これでVer. 1のコードが出力されました。しかし出力結果をもとのコードに合わせるためにデータ定義を変更したことで、元のコード自体が変わってしました!
当たり前といえば当たり前なのですが、このパラドックスがQuineを難しくしています。
データの中にデータ自体を埋め込む
Ver. 2をよく見ると、実はデータ定義が2箇所あることが分かるでしょうか?
1 | ↓ sのデータ定義 |
Quineを成立させる秘密は、外側で定義されたデータを使って、内側のデータ定義を埋めることです。
具体的には、Pythonのprint()内で%演算子を使うことで、文字列に値を挿入できます。
%演算子は以下のように、文字列に変数を展開するときに使います。
1 | print("%s 最高!" % "Python") |
これを先程のコードに適用すると、以下のようになります。
1 | # Ver 3 |
出力結果が元のコードとだいぶ似てきました。
このテクニックを使うことでQuineのパラドックスを解消することができます。
エスケープを頑張る
ここまで来るとQuineとしての基本的な概念はカバーしたと言えます。
ここからは(個人的には)Quineのめんどくさいところ、エスケープ処理を頑張っていきます。
Ver. 3を再確認します。
1 | # Ver. 3 (再掲) |
ここから、以下のような特殊文字をQuineできるように扱っていきます。
- 改行
- 引用符
- パーセント記号
私はコードをシンプルにするためQuineは1行にしてしまうのが良いと思っています。改行を含むコードは、たいていの言語で(Pythonのトリプルクオートなど)特殊な構文が必要になります。
そこで、改行を削除し、代わりにセミコロンを使用しました。トリプルクオートが不要になったので、シングルクオートに置き換えます。
1 | # Ver. 4 |
しかし、このコードは、シングルクオートの中にシングルクオートを使っているため、構文エラーになります。
通常のエスケープ(\'など)を使っても、出力されたコードが構文エラーになってしまうため、クワインではうまくいきません。
1 | # Ver. 4.1 |
この問題を解決する方法の1つが、%sでデータ展開をしたのと同じように、フォーマット文字列で%cを使うことです。
1 | # Ver. 4.2 |
マジックナンバーの39は、シングルクオートのASCIIコードです。
Ver. 4.2はほとんどQuineですね。残っているのは、新しく追加したデータ内の出力部分print(s%(39,s,39))です。
ここでの注意は、% 記号ですが、これは二重パーセント(%%)を使うだけでエスケープできます。
これでPythonでのQuineが完成しました!
1 | # Ver. 5 |
Quineリレー
Quineの書き方をマスターしたところで、次はQuineリレーを紹介します。
Quineリレーとは、ある言語のコードが出力され、それが別の言語のコードを出力し、それがまた別の言語のコードを出力…と続き、最終的に最初のコードを出力するプログラムです。
ここではPythonとPHPの間でQuineリレーを作成する過程を、順を追って見ていきます。
PHPを出力するPython
最初にシンプルなphpコードをPythonで出力してみます。
1 | # Ver. 1 |
出力された結果は、正しいPHPコードです。Quineリレーでは、Pythonから出力されたPHPコードに元のPythonコードを出力させるよう頑張っていきましょう。
変数sの内容を、より現実的なものに置き換えてみます。
1 | # Ver. 2 |
割といい感じですね。
最終的なPHPコードの出力結果を見ると、元のコードの最初の%sを、Pythonで定義された**s自身の値**で置き換えれば良いことがわかります。
自己複製するロジック
そこで、置き換えのロジックを追加しました。
1 | # Ver. 3 |
この部分が、自己複製を可能にするポイントです。
1 | print('<?php print("%s");' % (s%s)) |
(s%s)によって、sのなかの%sを自身で展開した結果が生成され、それがPHPコードの出力として埋め込まれます。
PHPが出力しようとしている文字列は、元のPythonコードにかなり近づきましたが、PHPコード自体のエスケープがまだ不完全です。これを修正します。
試行錯誤の末、以下のようなコードでエスケープを機能させることができました。
1 | # Ver. 4 |
ここでのポイントは、Pythonの%rフォーマッタです。これは%sに似ていますが、文字列を有効なPythonコードとして出力してくれるため、引用符のエスケープを自動的に処理してくれています。
コードを難読化する
オンラインにあるQuineの例は、多少難読化というか奇抜な感じになっていますよね。せっかくなので、私たちのQuineリレーも読みづらくしてみます。
まず、変数名sを_に変えることでヤバさを増やします。また、空白もすべて削除して読みにくくしましょう。
1 | # Ver. 5 |
結構やばくなってきましたね。
更にPHPでは、<?php print("something");の代わりに、<?="something"?>という短縮構文が使えるのでそれを利用しましょう。
これがQuineリレーの最終バージョンです。
1 | # Ver 5.1 |
まとめ
オンラインで公開されているQuineは、多くの場合、いかに奇妙にするか、アートのような要素が追求されています(それはそれで面白いのですが、概念の理解には不向きですね😅)。
この記事を通じて、この奇妙で美しい概念を、読みやすいコードで理解する手助けができたなら幸いです。
コードの難読化をしてみて思ったのですが、コードを難しくするのは意外と簡単ですが、それをした後は指数関数的に読むのが難しくなるという非対称性に気づきました。また、Quineのようなトリッキーなとっつきづらいコードの解説をどこかで書くかもしれません。
最後までお読みいただき、ありがとうございました😆
それじゃ今日はこの辺で。
PR
あなたの会社はあなたの技術を評価してくれていますか?
技術力を高めようと頑張っているのに、
技術が評価されないような会社にいたらそれは良い環境なのでしょうか?
エンジニアとして技術を高めたいのなら環境を選ぶことも大事です。
レバテックキャリア
エンジニアとして働いていて実務経験があるなら、
求人数の充実具合からレバテックキャリアがおすすめです。
IT転職ではデファクト・スタンダードですね。
▼レバテック キャリア 登録はこちら▼
Tech Clips
Tech Clipsは年収500万以上&自社サービスを持った会社に特化した求人サイトです。
首都圏限定になってはしまいますが、
収入を増やしたい、自社サービスを持った企業への転職をしたい人におすすめです。
参考資料
ご自身でQuineを書いてみたい方は、これらの記事が非常に参考になります。