はじめに
以下のようなスクリプトを作成し、Python3
で実行してみてください。プログラミングの力がある方は、実行前にどういった出力になるかを考えてみてください。
1 | _='_=%r;print(_%%_)';print(_%_) |
Quine
先ほどのスクリプトを実行すると、ソースコードそのものが出力されます。このようなプログラムのことをQuine、自己言及プログラムと呼びます。ただし、通常以下のようなプログラムはQuineと見なされません。
- 空のスクリプト
- ファイル入力を取るスクリプト
空のスクリプト
多くの言語では空のスクリプトを実行することができます。この時、当然標準出力には何も出力されないため、ソースコードそのものが出力となるスクリプト
と言えます。といっても、これではあまりにも面白みがなく、Quineとは見なさないのが一般的です。
ファイル入力を取るスクリプト
スクリプトそのものを読み込んで、標準出力するスクリプトもQuineとは見なされません。確かに、これも何でもありになってしまいます。
1 | import sys |
冒頭のコードの解説
はじめにお見せしたコードはやや難解です。これでどうしてQuineが実現できるのか考えてみます。
1 | _='_=%r;print(_%%_)';print(_%_) |
%rの意味
まずは、Python
におけるprintf形式の文字列書式化
について復習しましょう。C言語
などを触ったことがある方は見慣れた書式ですね。
以下のようなスクリプトがあるとします。
1 | price = 1100 |
printf形式の文字列書式化
で書き換えると
1 | price = 1100 |
このように、文字列の出力の中に変数の値を用いたい場合に利用できます。
さて、今の例で%s
とした箇所はPythonオブジェクトをstr()で変換する
ことを表しています。そのため、price
が数値型であっても文字列型であっても結果は変わりません。
1 | price = 1100 |
1 | price = '1100' |
ただし、%d
とした場合はprice
が文字列型だとエラーとなるので注意してください。
1 | price = 1100 |
1 | price = '1100' |
さて、話を冒頭のスクリプトに戻します。スクリプトでは%s
が用いられていました。
1 | price = '1100' |
1 | 合計は1100円です |
このように%r
とした場合はシングルクォート付きで出力されます。これは、%s
とした箇所はPythonオブジェクトをrepr()で変換する
ためです。本題からそれてしまうため、repr
の詳細についてはここでは解説しません。
さて、実際に%s
の挙動を追ってみます。ここでは簡単のために以下のように一部文字を書き換えます。
1 | _='_=%r;print(_%%_)';print(_%'A') |
実行結果は以下のようになります。
1 | _='A';print(_%_) |
A
がシングルクォート付きで出力されていますね。
print(%%)の意味
次に、print(_%%_)
について解説します。printf形式の文字列書式化
で以下のようなことが実現できると学びました。
1 | print('消費税は10%です') |
1 | 消費税は10%です |
しかしながら、以下のスクリプトはエラーとなります。
1 | print('消費税は%s%です' % 10) # これはエラー |
これはprintf形式の文字列書式化
を用いた場合、%
を出力したければエスケープをする必要があるためです。
1 | print('消費税は%s%%です' % 10) |
1 | 消費税は10%です |
これでOKです。
まとめ
さて、これで必要な材料は揃ったので、最後にもう一度冒頭のスクリプトの流れを追います。
1 | _='_=%r;print(_%%_)';print(_%_) |
まず、セミコロンの手前までで、変数_
に代入を行います。変数_
には文字列_=%r;print(_%%_)
が格納されています。
続いて、print(_%_)
で変数_
を出力します。ただし変数内でprintf形式の文字列書式化
、%r
が用いられています。この箇所は一旦保留して[?]
としましょう。
なので出力は以下のようになるはずです。%
の数が減ることに注意してください。
1 | _=[?];print(_%_) |
さて[?]には、変数_
の中身が代入されます。%r
ですのでシングルクォートが付きます。
1 | _='_=%r;print(_%%_)';print(_%_) |
スクリプトそのものと全く同じ出力となりましたね。
記事情報
- 投稿日:2020年6月7日
- 最終更新日:2020年6月7日