2012-01-15 72 views
15

我读了Ken Thompson的经典论文Reflections on Trusting Trust,其中他提示用户编写Quine作为他的论点的介绍(强烈推荐阅读)。编写奎因的“诀窍”是什么?

奎因是一种计算机程序,它不接受任何输入并生成其自己的源代码的副本作为唯一的输出。

简易方法很简单,就是想说:

print "[insert this program's source here]" 

但一个快速认为这是不可能的。我使用Python结束了writing one myself,但仍然无法解释“诀窍”。 我正在寻找一个很好的解释为什么奎因斯是可能的。

+0

你读到的页面(肯·汤普森的图灵奖演讲中的那一页)在我读完之后就已经被删除了:/感谢上帝,我做了一个备份...... – SasQ 2012-08-12 18:07:20

+1

@ SasQ它仍然适用于我 – paislee 2012-08-13 19:01:35

+0

是的,现在它也适用于我。那天我看到一个服务器消息,该对象没有找到或类似的东西,所以我认为他们删除它。 – SasQ 2012-08-15 10:31:20

回答

11

正常的技巧是使用printf使得格式字符串代表程序的结构,用字符串本身一个占位符来获得你所需要的递归:

标准的C例如,从http://www.nyx.net/~gthompso/quine.htm说明了这一点相当不错:

char*f="char*f=%c%s%c;main(){printf(f,34,f,34,10);}%c";main(){printf(f,34,f,34,10);} 

编辑:写这篇文章,我做了一些搜索的:http://www.madore.org/~david/computers/quine.html给出确切基内斯是什么非常好的,更多的理论,说明为什么他们的工作。

3

下面是我写的一个使用putchar而不是printf;因此它必须处理所有它自己的转义码。但它在所有C执行字符集中均为%100。

你应该能够看到,有一个在反映一个在程序文本本身,它从开始工作到结束工作改变了文本表示。 编写Qu因的技巧是克服这个“驼峰”,在那里你切换到挖掘你的方式的洞!您的选项受到文本表示和语言输出设施的限制。

#include <stdio.h> 

void with(char *s) { 
    for (; *s; s++) switch (*s) { 
    case '\n': putchar('\\'); putchar('n'); break; 
    case '\\': putchar('\\'); putchar('\\'); break; 
    case '\"': putchar('\\'); putchar('\"'); break; 
    default: putchar(*s); 
    } 
} 
void out(char *s) { for (; *s; s++) putchar(*s); } 
int main() { 
    char *a[] = { 
"#include <stdio.h>\n\n", 
"void with(char *s) {\n", 
" for (; *s; s++) switch (*s) {\n", 
" case '\\", 
"n': putchar('\\\\'); putchar('n'); break;\n", 
" case '\\\\': putchar('\\\\'); putchar('\\\\'); break;\n", 
" case '\\\"': putchar('\\\\'); putchar('\\\"'); break;\n", 
" default: putchar(*s);\n", 
" }\n}\n", 
"void out(char *s) { for (; *s; s++) putchar(*s); }\n", 
"int main() {\n", 
" char *a[] = {\n", 
NULL }, *b[] = { 
"NULL }, **p;\n", 
" for (p = a; *p; p++) out(*p);\n", 
" for (p = a; *p; p++) {\n", 
" putchar('\\\"');\n", 
" with(*p);\n", 
" putchar('\\\"'); putchar(','); putchar('\\", 
"n');\n", 
" }\n", 
" out(\"NULL }, *b[] = {\\", 
"n\");\n", 
" for (p = b; *p; p++) {\n", 
" putchar('\\\"');\n", 
" with(*p);\n", 
" putchar('\\\"'); putchar(','); putchar('\\", 
"n');\n", 
" }\n", 
" for (p = b; *p; p++) out(*p);\n", 
" return 0;\n", 
"}\n", 
NULL }, **p; 
    for (p = a; *p; p++) out(*p); 
    for (p = a; *p; p++) { 
    putchar('\"'); 
    with(*p); 
    putchar('\"'); putchar(','); putchar('\n'); 
    } 
    out("NULL }, *b[] = {\n"); 
    for (p = b; *p; p++) { 
    putchar('\"'); 
    with(*p); 
    putchar('\"'); putchar(','); putchar('\n'); 
    } 
    for (p = b; *p; p++) out(*p); 
    return 0; 
} 

一个常见的技巧是跳跃通过编写一个程序来读取一个文本和输出数字的数组开始的喹。然后修改它以使用静态数组,然后针对新的(静态数组)程序运行第一个程序,从而生成一个表示程序的数字数组。将它插入到静态数组中,再次运行它直到它稳定下来,并且让你成为一个quine。 但是,它绑定到一个特定的字符集(==不是100%便携式)。像上面这样的程序(而不是经典的printf黑客)将在ASCII或EBCDIC上工作(传统的printf黑客在EBCDIC中失败,因为它包含硬编码的ASCII)。


编辑:

重读的问题,仔细(最终),看来你实际上是寻找更多的理念,少技术。让你走出无限倒退的诀窍是双发。您必须从相同的数据中获得编码的程序和扩展的程序:使用相同的数据2种方式。因此该数据仅描述了围绕其未来表现的部分程序框架。该帧内的图像是原始的直接副本。

这就是你自然会用手制作递归图的方式:电视电视的电视。在某些时候你会感到疲倦,只是在屏幕上画一些眩光,因为递归已经足够建立。


编辑:

我在寻找的,为什么基内斯是可能的一个很好的解释。

奎因的“可能性”进入了19世纪和20世纪的数学革命的深处。 “经典”奎因由WVO奎因,是词的序列(IIRC)

时追加到自身

这是一个悖论,如同大卫的要求的东西,“让我产生false “当伤心的时候快乐,让我开心的时候很难过”,双方都写着奖章回答:“这也会过去”。

同一种由现代数学逻辑的先驱,如弗雷格,拉塞尔和怀特黑德,瓦鲁萨维奇,当然,我们的男孩图灵,教堂和Thue调查。这个诀窍使得我们可以将Qu因从文字游戏转化为程序性演示(沿着路线解开悖论部分),这是Gödel将算术运算本身编码为数字的方法,因此整个数学表达式可以编码成一个单一的(巨大的)整数。具体地说,执行该表示的解码的数学函数可以以相同(数字)的形式表示。这个数字(一个哥德尔编码函数)的代码和数据。

这个三重奏(代码,表示,数据)可以转换为不同的表达方式。通过选择不同的表示(或如:bytes-> ASCII-> hexadecimal-> integer)会更改代码的行为,这会改变数据的外观。

相关问题