Quine(コンピューティング)

quine の出力はソース コードとまったく同じです。

クワインとは、入力を一切受け取らず、自身のソースコードのコピーのみを出力するコンピュータプログラムです。計算可能性理論コンピュータ科学の文献では、これらのプログラムを「自己複製プログラム」、「自己増殖プログラム」、「自己複製プログラム」といった 標準的な用語で表現しています。

クインとは、実行環境をプログラムをその出力に変換する関数とみなした場合の、その実行環境の不動点である。クインは、クリーネの再帰定理の直接的な帰結として、チューリング完全なプログラミング言語であればどこでも実現可能である。プログラマーは、娯楽として、特定のプログラミング言語で可能な限り最短のクインを開発しようとすることがある。

名前

「クワイン」という名前は、ダグラス・ホフスタッターが1979年に出版した人気科学書『ゲーデル、エッシャー、バッハ』の中で、間接的な自己言及について広範な研究を行った哲学者ウィラード・ヴァン・オーマン・クワイン(1908年 - 2000年)に敬意を表して名付けられました。特に、クワインのパラドックスとして知られる次のようなパラドックスを生み出す表現にちなんで名付けられました。

「引用符が先行すると虚偽となる」は引用符が先行すると虚偽となる。

歴史

ジョン・フォン・ノイマンは1940年代に自己増殖オートマトンを理論化しました。その後、ポール・ブラットリーとジャン・ミロは1972年に論文「コンピュータレクリエーション:自己増殖オートマトン」で自己増殖オートマトンについて論じました。 [ 1 ]ブラットリーが自己増殖プログラムに興味を持つようになったのは、1960年代にエディンバラ大学の講師兼研究者であるハミッシュ・デュワーがエディンバラ大学でAtlas Autocode に書いた最初の自己増殖プログラムを見たことがきっかけでした。

GNUアフェロ一般公衆利用許諾書の「ソースコードのダウンロード」要件は、クワインの考え方に基づいています。[ 2 ]

最短の適切なクイン

2020年現在、主要なプログラミング言語における最短の非自明なクイン(「適切な」クイン)は、21文字のJavaScript ES6プログラムである($=_=>`($=${$})()`)()[ 3 ]

建設的なクイン

一般的に、どのプログラミング言語でもクインを作成する方法は、プログラム内に2つの部分、つまり実際の印刷を行うためのコードと、コードのテキスト形式を表すデータを配置することです。コードは、データを使用してコードを印刷することで機能します(データがコードのテキスト形式を表しているため、これは理にかなっています)。また、単純な方法で処理されたデータを使用して、データ自体のテキスト表現を印刷することもできます。

以下に Python3 の 3 つの小さな例を示します。

# 例A. chr(39) == "'". a : str = 'a: str = {}{}{} ; print(a.format(chr(39), a, chr(39)))' ; print ( a . format ( chr ( 39 ), a , chr ( 39 )))
# 例B. chr(39) == "'". b : str = 'b: str = %s%s%s ; print(b %% (chr(39), b, chr(39)))' ; print ( b % ( chr ( 39 ), b , chr ( 39 )))
# 例 C. %r は自動的に引用符で囲みます。c : str = 'c: str = %r ; print(c %% c)' ; print ( c % c )

次の古典的なJavaコード (Java 1.5) は、クワインの基本構造を示しています。

public class Quine { public static void main ( String [] args ) { char q = 34 ; // 引用符文字String [] l = { // ソースコードの配列"public class Quine {" , " public static void main(String[] args) {" , " char q = 34; // 引用符文字" , " String[] l = { // ソースコードの配列" , " " , " };" , " for (int i = 0; i < 4; i++) { // 開始コードを出力" , " System.out.println(l[i]);" , " }" , " for (int i = 0; i < l.length; i++) { // 文字列配列を出力" , " System.out.printf(\"%s%c%s%c,%n\", l[4], q, l[i], q);" , " }" , " for (int i = 5; i < l.length; i++) { // このコードを出力します" , " System.out.println(l[i]);" , " }" , " }" , "}" , " }" , } ; for ( int i = 0 ; i < 4 ; i ++ ) { // 開始コードを出力しますSystem.out.println ( l [ i ] ) ; } for ( int i = 0 ; i < l.length ; i ++ ) { //文字配列を出力ますSystem.out.printf ( " %s%c%s%c,%n" , l [ 4 ] , q , l [ i ] , q ); } for ( int i = 5 ; i < l.length ; i ++ ) { //このコードを出力ますSystem .アウト.println ( l [] ); } } }

ソース コードには、それ自体の文字列配列が含まれており、引用符で囲まれて 1 回ずつ、2 回出力されます。

このコードはc2.comのオリジナル記事から改変したもので、作者のJason WilsonがJavaコメントなしのQuineの最小限のバージョンとして投稿しました。[ 4 ]

Java 15でテキストブロック機能が導入されたことで[ 5 ] 、より読みやすくシンプルなバージョンが可能になりました。[ 6 ]

パブリッククラスQuine {パブリックスタティックvoid main ( String [] args ) { String textBlockQuotes = new String ( new char [] { '"' , '"' , '"' }); char newLine = 10 ; String source = """パブリック クラス Quine { パブリック スタティック void main(String[] args) {  String textBlockQuotes = new String(new char[]{'"', '"', '"'});  char newLine = 10;  String source = "%s%s%s%s"; System.out.print ( source.formatted ( textBlockQuotes  , newLine, source, textBlockQuotes));  } } """ ; System.out.print ( source.formatted ( textBlockQuotes , newLine , source , textBlockQuotes )) ; } }

これはJava 25 コード の最新バージョンの例です。

void main () {文字列s = """  void main() { 文字列 s = %c%c%c  %s%c%c%c; IO.print(s.formatted(34, 34, 34, s, 34 ,  34, 34)); }  """ ; IO .print ( s.formatted ( 34 , 34 , 34 , s , 34 , 34 , 34 ) ); }

上記のコードは、その内容を「 App.java 」などの.java拡張子を持つ任意のファイルにドロップし、「 java App.java 」で実行するだけで実行できます。

同じ考え方が次のSQLクインでも使用されています。

SELECT REPLACE ( REPLACE ( 'SELECT REPLACE(REPLACE("$",CHAR(34),CHAR(39)),CHAR(36),"$") AS Quine' , CHAR ( 34 ), CHAR ( 39 )), CHAR ( 36 ), 'SELECT REPLACE(REPLACE("$",CHAR(34),CHAR(39)),CHAR(36),"$") AS Quine' ) AS Quine

評価クイン

一部のプログラミング言語には、文字列をプログラムとして評価する機能があります。Quine はこの機能を利用できます。例えば、次のRuby のQuine は次のようになります。

eval s = "print 'eval s=';p s"

Lua では次のことが可能です:

s = "print(string.format('s=%c%s%c; load(s)()',34,s,34))" ; load ( s )()

Python 3.8 の場合:

exec ( s := 'print("exec(s:= %r )" %s )' )

「不正行為」クイン

自己評価

Schemeやその他のLisp言語、そしてAPLなどの対話型言語を含む多くの関数型言語では、数値は自己評価型です。TI -BASICでは、プログラムの最終行が値を返す場合、返された値は画面に表示されます。そのため、これらの言語では、1桁の数字のみで構成されるプログラムは1バイトのクイン(quine)になります。このようなコードは自己構築されないため、しばしば不正行為とみなされます。

1

空のクイン

一部の言語、特にスクリプト言語では、空のソースファイルは言語の固定点であり、出力を生成しない有効なプログラムです。[ a ]

このような空のプログラムは、「世界最小の自己複製プログラム」として提出され、かつて国際難読化Cコードコンテストで「最悪のルール違反」賞を受賞した。[ 7 ]このプログラムは有効なC言語ではなかった(main()関数がない)ため、実際にはコンパイルされていなかったが、空のファイルを別のファイルにコピーするMakefileスクリプトが付属しており、このスクリプトはシェルスクリプトとして実行され、何も印刷されなかった。[ 8 ]cp

ソースコード検査

クインは定義上、ファイルの読み取りを含め、いかなる形式の入力も受け付けません。つまり、クインが自身のソースコードを参照することは「不正行為」とみなされます。以下のシェルスクリプトはクインではありません。

#!/bin/sh # 無効な quine。# 実行されたファイルをディスクから読み取るのは不正行為です。cat $ 0

シェバンディレクティブ の動作を利用した、より短いバリエーション:

#!/bin/cat

その他の問題のある手法としては、コンパイラ メッセージを利用することが挙げられます。たとえば、GW-BASIC環境では、「構文エラー」と入力すると、インタープリタは「構文エラー」と応答します。

Quine コードは視覚的に出力することもできます。たとえば、構文サッカリンとともにYars' Revengeの中立ゾーンを視覚化してソース コードを難読化するために使用されます。

ウロボロス計画

クワインの概念は複数レベルの再帰に拡張することができ、「ウロボロスプログラム」またはクワインリレーを生み出します。これはマルチクワインと混同しないでください。

この Java プログラムは、元の Java コードを出力する C++ プログラムのソースを出力します。

stdをインポートしますint main ( int argc , char * argv []) { char q = 34 ; std :: array < std :: string , 36 > l = { " " , "============<<<<<<<< C++ コード >>>>>>>>===============" , "import std;" "" , "int main(int argc, char* argv[]) {" , " char q = 34;" , " std::array<std::string> l = {" , " };" , " for (int i = 19; i <= l.size(); ++i) {" , " std::println( \" {} \" , l[i]);" , " }" , " for (int i = 0; i <= l.size(); ++i)" , " std::println( \" {}{}{}{}, \" , l[0], q, l[i], q);" , " }" , " for (int i = 2; i <= 17; ++i)" , " std::println( \" {} \" , l[i]);" , " }" , " return 0;" , "}" , "============<<<<<<< Java コード >>>>>>>>===============" , "public class Quine {" , " public static void main(String[] args) {" , " char q = 34;" , " String[] l = {" , " };" , " for (int i = 2; i <= 17; ++i)" , " System.out.println(l[i]);" , " }" , " for (int i = 0; i < l.length; ++i) {" , " System.out.printf( \" %s%c%s%c,%n \" , l[0], q, l[i], q);" , " }" , " for (int i = 18; i <= l.length; ++i) {" , " System.out.println(l[i]);" , " }" , " }" , "}" ,}; for ( int i = 19 ; i <= l . size (); ++ i ) std :: println ( "{}" , l [ i ]);; }for ( int i = 0 ; i <= l . size (); ++ i ) std :: println ( "{}{}{}{}," , l [ 0 ], q , l [ i ], q ); } for ( int i = 2 ; i <= 17 ; ++ i ) std :: println ( "{}" , l [ i ] );; } 0を返します; }
public class Quine { public static void main ( String [] args ) { char q = 34 ; String [] l = { " " , "=============<<<<<<<< C++ コード >>>>>>>>===============" , "import std;" , "" , "int main(int argc, char* argv[]) {" , " char q = 34;" , " std::array<std::string> l = {" , " };" , " for (int i = 19; i <= l.size(); ++i) {" , " std::println(\"{}\", l[i]);" , " for (int i = 0; i <= l.size(); ++i) {" , " std::println(\"{}{}{}{},\", l[0], q, l[i], q);" , " for (int i = 2; i <= 17; ++i) {" , " std::println(\"{}\", l[i]);" , " }" , " return 0;" , "}" , "============<<<<<<< Java コード >>>>>>>>==============" , "public class Quine {" , " public static void main(String[] args) {" , " char q = 34;" , " String[] l = {" , " };" , " for (int i = 2; i <= 17; ++i)" , " System.out.println(l[i]);" , " for (int i = 0; i < l.length; ++i)" , " System.out.printf(\"%s%c%s%c,%n\", l[0], q, l[i], q);" , " for (int i = 18; i <= l.length; ++i) {" , " System.out.println(l[i]);" , " }" " }" , "}" , }; for ( int i = 2 ; i <= 17 ; ++ i ) { System . out .println ( l [ i ] ); } for ( int i = 0 ; i < l . length ; ++ i ) { System . out . printf ( "%s%c%s%c,%n" , l [0 ] , q , l [ i ] , q ); } for ( int i = 18 ; i <= l . length ; ++ i ) { System . out . println ( l [ i ] ); } } }

このようなプログラムは、さまざまなサイクルの長さで作成されています。

マルチキネス

Unlambdaの開発者であるDavid Madoreは、マルチキネについて次のように説明しています。[ 18 ]

マルチクインとは、r個の異なるプログラム(r個の異なる言語で記述されているプログラム。この条件がなければ、これら全てを単一のクインとみなすこともできます)の集合です。各プログラムは、渡されたコマンドライン引数に応じて、r個のプログラムのいずれか(自身を含む)を出力できます。(不正行為は許可されていません。コマンドライン引数は長すぎてはなりません。プログラムの全文を渡すことは不正行為とみなされます。)

2 つの言語で構成されるマルチクイン (またはバイクイン) は次のようなプログラムです。

  • 実行すると、言語 X のクインになります。
  • ユーザー定義のコマンドライン引数を指定すると、言語 Y で 2 番目のプログラムを出力します。
  • 言語 Y の 2 番目のプログラムを通常どおり実行すると、これも言語 Y のクインになります。
  • 言語 Y の 2 番目のプログラムに、ユーザー定義のコマンドライン引数を指定すると、言語 X の元のプログラムが生成されます。

バイキンは 2 つのプログラムのセットとして考えることができ、どちらのプログラムも、指定されたコマンド ライン引数に応じて、どちらか一方を印刷できます。

理論的には、マルチクインに含まれる言語の数に制限はありません。5つの言語からなるマルチクイン(またはペンタクイン)は、PythonPerlCNewLISPF#で作成されています[ 19 ]。 また、25言語からなるマルチクインも存在します[ 20 ] 。

多言語話者

マルチクインと似ていますが、ポリグロットプログラムとは異なり、複数のプログラミング言語またはファイル形式の構文を組み合わせることで、それらの有効な形式で記述されたコンピュータプログラムまたはスクリプトです。ポリグロットプログラムは自己複製性を持つ必要はありませんが、実行方法の1つ以上においてクインとなることもあります。

クインやマルチクインとは異なり、ポリグロット プログラムは、クリーネの再帰定理の結果として、任意の言語セット間に存在することが保証されません。これは、ポリグロット プログラムが構文間の相互作用に依存しており、常に 1 つの構文が別の構文に埋め込まれるという証明可能な特性に依存していないためです。

放射線耐性

放射線耐性クインとは、任意の1文字を削除しても元のプログラムが再現され、文字が欠落していない状態を維持するクインのことである。必然的に、このようなクインは通常のクインよりもはるかに複雑であり、Rubyにおける以下の例からもそれがわかる。[ 21 ]

eval = 'eval$q=%q(puts %q(10210/ #{ 1 1 if 1 == 21 } }/.i rescue##/1 1"[13,213].max_by{|s|s.size}#"##").gsub(/\d/){["= \47 eval$q=%q( #$q )# \47 ## \47",:eval,:instance_,"||=9"][eval$&]} exit)#' ##'instance_eval = 'eval$q=%q(puts %q(10210/ #{ 1 1 if 1 == 21 } }/.i rescue##/1 1"[13,213].max_by{|s|s.size}#"##").gsub(/\d/){["= \47 eval$q=%q( #$q )# \47 ## \47",:eval,:instance_,"||=9"][eval$&]} exit)#' ##'/ #{ eval eval if eval == instance_eval } }/ . i rescue ##/評価評価"[eval||=9,instance_eval||=9].max_by{|s|s.size}#" ##"

自動生成

リレーショナルプログラミング技術を使用すると、言語のインタープリタ(または同等のコンパイラとランタイム)をリレーショナルプログラムに変換し、固定点を解くことで、クインを自動的に生成することができます。[ 22 ]

参照

変種

ハッシュキネス

ハッシュクインとは、独自の暗号ハッシュを含むファイルです。クインと関連がありますが、動作は異なります。クインは独自のソースコードを出力するプログラムですが、ハッシュクインは通常、画像やバイナリなどの静的ファイルで、暗号ダイジェスト(例えばSHA-256)がファイル自体の内部に埋め込まれるように構築されています。

ハッシュキーの作成には通常、ファイルに調整可能なバイト領域を与え、最終的なハッシュがファイルに埋め込まれた値と一致する値を検索します。一部のハッシュキーは画像として表示され、表示されるハッシュが画像の一部であり、画像全体も同じハッシュ値になります。

ハッシュクインは何も実行したり出力したりしません。暗号学的ハッシュ関数における固定小数点を表すものであり、厳密なプログラミング上の意味でのクインの一種ではなく、クインの概念的な類似物とみなされます。

注記

  1. ^例としては、 Bash Perl Pythonなどがある。

参考文献

  1. ^ Bratley, Paul ; Millo, Jean (1972). 「コンピュータレクリエーション:自己増殖オートマトン」.ソフトウェア:実践と経験. 2 (4): 397– 400. doi : 10.1002/spe.4380020411 . S2CID  222194376 .
  2. ^ Kuhn, Bradley M. (2007年11月21日). 「stetとAGPLv3」 . Software Freedom Law Center. 2008年3月15日時点のオリジナルよりアーカイブ2008年6月14日閲覧。
  3. ^アルマン、ベン (2020 年 12 月). 「GitHub 要点」
  4. ^ 「クワイン計画」 .wiki.c2.com .
  5. ^ "JEP 378: テキストブロック" . openjdk.org . 2025年10月12日閲覧
  6. ^ 「シンプルなJavaクイン、テキストブロック付きの自己複製(自己コピー)Javaコード。このコードは、特別なフラグを付けたJava 15以降またはJava 13以降で実行できます。ライセンスはパブリックドメインであり、権利は留保されていません。」
  7. ^ノル、ランドン・カート「1994/smr - 最悪のルール違反」 IOCCC 2025年10月11日閲覧
  8. ^ “Makefile” . IOCCC.org . 2019年4月23日時点のオリジナルよりアーカイブ2019年4月4日閲覧。
  9. ^ Dan Piponi (2008年2月5日). 「3つの言語による第三階クワイン」 .
  10. ^ブルース・エディガー. 「求めよ、汝は与えられる:3世代、Python、Bash、Perlを経て自己複製するプログラム」 . 2011年2月23日時点のオリジナルよりアーカイブ。 2011年3月17日閲覧
  11. ^ bm (2011年2月1日). 「multiquine」 . 2013年4月15日時点のオリジナルよりアーカイブ
  12. ^ダン・ピポニ (2011 年 1 月 30 日)。「クワイン・セントラル」
  13. ^ Ruslan Ibragimov (2013年4月20日). “Quine Ruby -> Java -> C# -> Python” (ロシア語). 2016年3月4日時点のオリジナルよりアーカイブ。 2013年4月20日閲覧
  14. ^浜地真一郎 (2007年11月10日). 「shinhによるQuine (C C++ Ruby Python PHP Perl)」 .(これも多言語話者です)
  15. ^ Ku-ma-me (2009年9月22日). 「Uroborosプログラミング:11のプログラミング言語」 . 2011年8月29日時点のオリジナルよりアーカイブ2011年3月17日閲覧。
  16. ^ Yusuke Endoh (2021年11月2日). 「Quine Relay - 100以上のプログラミング言語で動作するuroborosプログラム」 . GitHub .
  17. ^ Michael Wehar (2019年11月10日). 「C Prints JavaScript」 .
  18. ^デヴィッド・マドール。「クイン(自己複製プログラム)」
  19. ^ライナード・ヴァン・トンダー (2020 年 1 月 14 日)。「ペンタキン - 5 部マルチキン」GitHub
  20. ^ Lu Wang (2021年5月21日). 「Quine Chameleon#Variants」 . GitHub .
  21. ^ Yusuke Endoh. 「放射線耐性強化クワイン」 . GitHub . 2014年2月24日閲覧
  22. ^ Byrd, William E.; Holk, Eric; Friedman, Daniel P. (2012-09-09). 「MiniKanren, live and untagged: Quine generation via relational interpreters (Programming pearl)」(PDF) . Proceedings of the 2012 Annual Workshop on Scheme and Functional Programming . Scheme '12. New York, NY, USA: Association for Computing Machinery. pp.  8– 29. doi : 10.1145/2661103.2661105 . ISBN 978-1-4503-1895-2

さらに読む