Quine
以哲学家奎恩(Willard Van Orman Quine) 命名,指的是输出结果为程序自身源码的程序,也称为 自产生程序
。有点类似于代码中的不动点,举一个例子
quine.py 1 exec (s:='print("exec(s:=%r)"%s)' )
那么以下命令全部输出 exec(s:='print("exec(s:=%r)"%s)')
1 2 3 python quine.py python -c $(python quine.py) python -c $(python -c $(python quine.py))
理论上图灵完备的语言都可以构造出 Quine
,那么如何在 Java 中构造呢?
维基百科的例子 下面的例子来自维基百科 https://en.wikipedia.org/wiki/Quine_(computing)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 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; // Quotation mark character" , " String[] l = { // Array of source code" , " " , " };" , " for(int i = 0; i < 6; i++) // Print opening code" , " System.out.println(l[i]);" , " for(int i = 0; i < l.length; i++) // Print string array" , " System.out.println(l[6] + q + l[i] + q + ',');" , " for(int i = 7; i < l.length; i++) // Print this code" , " System.out.println(l[i]);" , " }" , "}" , }; for (int i = 0 ; i < 6 ; i++) System.out.println(l[i]); for (int i = 0 ; i < l.length; i++) System.out.println(l[6 ] + q + l[i] + q + ',' ); for (int i = 7 ; i < l.length; i++) System.out.println(l[i]); } }
大概的原理是(是一个通用的模式)
在代码中预留一个字符串数组,每个元素代表一行代码
先输出代码到数组之前 String[] l = {
的行
再输出数组(需要处理缩进)
再输出剩下的行
最后把这个代码粘贴到数组中
还有一个 Java 15 及以上(text blocks
)的例子
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 public class Quine { public static void main (String[] args) { String textBlockQuotes = new String (new char []{'"' , '"' , '"' }); char newLine = 10 ; String source = """ public class Quine { public static void main(String[] args) { String textBlockQuotes = new String(new char[]{'"', '"', '"'}); char newLine = 10; String source = %s; System.out.print(source.formatted(textBlockQuotes + newLine + source + textBlockQuotes)); } } """ ; System.out.print(source.formatted(textBlockQuotes + newLine + source + textBlockQuotes)); } }
转义字符 上面的例子是通过 %s 替换自身代码来完成的,之前的 Java 版本中需要对换行和双引号进行转义。按照这个思路可以替换成如下实现(通过 char 跳过转义)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 public class Quine { public static void main (String[] args) { char quote = 34 ; char [] replace = {92 ,'n' ,34 ,10 ,32 ,32 ,32 ,32 ,32 ,32 ,32 ,32 ,32 ,32 ,32 ,32 ,'+' ,32 ,34 }; String source = "public class Quine {\n" + " public static void main(String[] args) {\n" + " char quote = 34;\n" + " char[] replace = {92,'n',34,10,32,32,32,32,32,32,32,32,32,32,32,32,'+',32,34};\n" + " String source = %s;\n" + " String self = source.replace(String.valueOf((char)10), new String(replace));\n" + " System.out.print(String.format(source, quote + self + quote));\n" + " }\n" + "}" ; String self = source.replace(String.valueOf((char )10 ), new String (replace)); System.out.print(String.format(source, quote + self + quote)); } }
代码会有点麻烦(为了保留格式),上面 replace 代码相当于
1 source.replace("\n" , "\\n\"\n + \"" )
如果不考虑格式和换行的话(只能写在一行了)
1 class Q {public static void main (String[]a) {String s="class Q{public static void main(String[]a){String s=%c%s%1$c;System.out.printf(s,34,s);}}" ;System.out.printf(s,34 ,s);}}
这里还有更多的例子 http://www.nyx.net/~gthompso/self_java.txt 。
还有一种更加通用的方案,通过编码的方式绕过转义字符的影响,参考 https://www.zhihu.com/question/22006572/answer/47923822
先写一个通用的代码
1 2 3 4 5 6 7 public class Quine { public static void main (String[] args) { String s = "__" ; System.out.println(java.net.URLDecoder.decode(s).replaceFirst("__" , s)); } }
然后把上面的代码通过 URLEncoder
编码,然后替换引号里面
1 2 3 4 5 6 7 public class Quine { public static void main (String[] args) { String s = "public+class+Quine+%7B%0A++++public+static+void+main%28String%5B%5D+args%29+%7B%0A++++++++String+s+%3D+%22__%22%3B%0A++++++++%2F%2F+%E9%80%9A%E8%BF%87+URLDecoder%0A++++++++System.out.println%28java.net.URLDecoder.decode%28s%29.replaceFirst%28%22__%22%2C+s%29%29%3B%0A++++%7D%0A%7D" ; System.out.println(java.net.URLDecoder.decode(s).replaceFirst("__" , s)); } }
同理
1 2 3 4 5 6 7 public class Quine { public static void main (String[] args) { String s = "cHVibGljIGNsYXNzIFF1aW5lIHsKICAgIHB1YmxpYyBzdGF0aWMgdm9pZCBtYWluKFN0cmluZ1tdIGFyZ3MpIHsKICAgICAgICBTdHJpbmcgcyA9ICJfXyI7CiAgICAgICAgLy8g6YCa6L+HIEJhc2U2NAogICAgICAgIFN5c3RlbS5vdXQucHJpbnRsbihuZXcgU3RyaW5nKGphdmEudXRpbC5CYXNlNjQuZ2V0RGVjb2RlcigpLmRlY29kZShzKSkucmVwbGFjZUZpcnN0KCJfXyIsIHMpKTsKICAgIH0KfQ==" ; System.out.println(new String (java.util.Base64.getDecoder().decode(s)).replaceFirst("__" , s)); } }
Ouroboros programs
public class Quine { public static void main (String[] args) { char q = 34 ; String[] l = { " " , "=============<<<<<<<< C++ Code >>>>>>>>=============" , "#include <iostream>" , "#include <string>" , "using namespace std;" , "" , "int main(int argc, char* argv[])" , "{" , " char q = 34;" , " string l[] = {" , " };" , " for(int i = 20; i <= 25; i++)" , " cout << l[i] << endl;" , " for(int i = 0; i <= 34; i++)" , " cout << l[0] + q + l[i] + q + ',' << endl;" , " for(int i = 26; i <= 34; i++)" , " cout << l[i] << endl;" , " return 0;" , "}" , "=============<<<<<<<< Java Code >>>>>>>>==========" , "public class Quine" , "{" , " public static void main( String[] args )" , " {" , " char q = 34;" , " String[] l = {" , " };" , " for(int i = 2; i <= 9; i++)" , " System.out.println(l[i]);" , " for(int i = 0; i < l.length; i++)" , " System.out.println( l[0] + q + l[i] + q + ',' );" , " for(int i = 10; i <= 18; i++))" , " System.out.println(l[i]);" , " }" , "}" , }; for (int i = 2 ; i <= 9 ; i++) System.out.println(l[i]); for (int i = 0 ; i < l.length; i++) System.out.println( l[0 ] + q + l[i] + q + ',' ); for (int i = 10 ; i <= 18 ; i++) System.out.println(l[i]); } }
#include <iostream> #include <string> using namespace std;int main (int argc, char * argv[]) { char q = 34 ; string l[] = { " " , "=============<<<<<<<< C++ Code >>>>>>>>=============" , "#include <iostream>" , "#include <string>" , "using namespace std;" , "" , "int main(int argc, char* argv[])" , "{" , " char q = 34;" , " string l[] = {" , " };" , " for(int i = 20; i <= 25; i++)" , " cout << l[i] << endl;" , " for(int i = 0; i <= 34; i++)" , " cout << l[0] + q + l[i] + q + ',' << endl;" , " for(int i = 26; i <= 34; i++)" , " cout << l[i] << endl;" , " return 0;" , "}" , "=============<<<<<<<< Java Code >>>>>>>>=============" , "public class Quine" , "{" , " public static void main(String[] args)" , " {" , " char q = 34;" , " String[] l = {" , " };" , " for(int i = 2; i <= 9; i++)" , " System.out.println( l[i] );" , " for(int i = 0; i < l.length; i++)" , " System.out.println(l[0] + q + l[i] + q + ',');" , " for(int i = 10; i <= 18; i++)" , " System.out.println(l[i]);" , " }" , "}" , }; for (int i = 20 ; i <= 25 ; i++) cout << l[i] << endl; for (int i = 0 ; i <= 34 ; i++) cout << l[0 ] + q + l[i] + q + ',' << endl; for (int i = 26 ; i <= 34 ; i++) cout << l[i] << endl; return 0 ; }
这个例子来自维基百科 https://en.wikipedia.org/wiki/Quine_(computing)
两段代码结构上非常相似,而且跟第一个例子也很像。Java 的代码输出 C++ 的代码,C++ 的代码输出 Java 的代码,循环生成。
也可以用上面占位符的思路,这里演示多个 Java 代码相互生成的例子,class A
-> class B
-> class C
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 public class A { public static void main (String[] args) { String s = "public+class A+%7B%0A++++public+static+void+main%28String%5B%5D+args%29+%7B%0A++++++++String+s+%3D+%22__%22%3B%0A++++++++String+code+%3D+java.net.URLDecoder.decode%28s%29.replaceFirst%28%22__%22%2C+s%29%3B%0A++++++++String+a+%3D+%22class+%22+%2B+%22A%22%3B%0A++++++++String+b+%3D+%22class+%22+%2B+%22B%22%3B%0A++++++++String+c+%3D+%22class+%22+%2B+%22C%22%3B%0A++++++++if+%28code.contains%28a%29%29+%7B%0A++++++++++++code+%3D+code.replaceAll%28a%2C+b%29%3B%0A++++++++%7D+else+if+%28code.contains%28b%29%29+%7B%0A++++++++++++code+%3D+code.replaceAll%28b%2C+c%29%3B%0A++++++++%7D%0A++++++++System.out.println%28code%29%3B%0A++++%7D%0A%7D" ; String code = java.net.URLDecoder.decode(s).replaceFirst("__" , s); String a = "class " + "A" ; String b = "class " + "B" ; String c = "class " + "C" ; if (code.contains(a)) { code = code.replaceAll(a, b); } else if (code.contains(b)) { code = code.replaceAll(b, c); } System.out.println(code); } }
其他语言的例子 下面的例子来自 https://rosettacode.org/wiki/Quine
C 1 2 #include <stdio.h> int main () {char *c="#include <stdio.h>%cint main(){char*c=%c%s%c;printf(c,10,34,c,34,10);return 0;}%c" ;printf (c,10 ,34 ,c,34 ,10 );return 0 ;}
Go 1 2 3 4 5 6 7 8 package main import "fmt" func main () { a := "package main\n\nimport \"fmt\"\n\nfunc main() {\n\ta := %q\n\tfmt.Printf(a, a)\n}\n" fmt.Printf(a, a) }
JavaScript
Quine Relay https://github.com/mame/quine-relay
This is a Ruby program that generates Rust program that generates Scala program that generates …(through 128 languages in total)… REXX program that generates the original Ruby code again.
多种语言的 Quine
循环, Ruby
-> Rust
-> Scala
-> ...
-> Ruby
最终生成跟原来相同的 Ruby
程序,一共 128
种语言。原理可以参考 https://www.zhihu.com/question/21568155 。
参考 https://en.wikipedia.org/wiki/Quine_(computing) http://www.nyx.net/~gthompso/quine.htm https://www.zhihu.com/question/22006572 https://github.com/mame/quine-relay https://rosettacode.org/wiki/Quine