更改
跳到导航
跳到搜索
第173行:
第173行:
− +
− +
− +
第187行:
第187行:
− +
−
−
−
− +
第201行:
第198行:
− +
− +
− <code>B</code>+
+
第228行:
第226行:
− +
−
−
−
− +
第269行:
第264行:
+
第275行:
第271行:
− +
+
+
无编辑摘要
所谓的程序自打印就是指一个程序能够在'''不读取外部文件'''的条件下把自己的源代码打印出来。首先,我们要先领教一下,一个自我打印的程序是多么不可能的!我们知道,要写一个程序打印出“helloworld!”字样是非常容易的,例如:
所谓的程序自打印就是指一个程序能够在'''不读取外部文件'''的条件下把自己的源代码打印出来。首先,我们要先领教一下,一个自我打印的程序是多么不可能的!我们知道,要写一个程序打印出“helloworld!”字样是非常容易的,例如:
<code>Print(‘helloworld!’)</code>
<pre>Print(‘helloworld!’)</pre>
注意在这个程序中,字符串都用单引号括起来。那么,我们能不能写一个程序,把这个打印“helloworld!”程序的源代码打印出来呢?这也是可以办到的,例如下面的程序:
注意在这个程序中,字符串都用单引号括起来。那么,我们能不能写一个程序,把这个打印“helloworld!”程序的源代码打印出来呢?这也是可以办到的,例如下面的程序:
<code>Print(‘Print(\’helloworld!\’)’)</code>
<pre>Print(‘Print(\’helloworld!\’)’)</pre>
注意,这里面的“\’”会被编译器解释为一个字符串,这个字符串中就有一个字符:“ ` ”。采用这个技巧,我们就可以解决如何在一个引号之中再输入一个引号的问题了。所以,我们可以很轻松地打印出这个能够打印”helloworld!”程序的程序源代码出来。但是很显然这个程序并不能打印出它自己,也许你会想到能不能打印出上面的程序源代码出来?当然可以!
注意,这里面的“\’”会被编译器解释为一个字符串,这个字符串中就有一个字符:“ ` ”。采用这个技巧,我们就可以解决如何在一个引号之中再输入一个引号的问题了。所以,我们可以很轻松地打印出这个能够打印”helloworld!”程序的程序源代码出来。但是很显然这个程序并不能打印出它自己,也许你会想到能不能打印出上面的程序源代码出来?当然可以!
<code>Print(‘Print(\’Print(\\\’helloworld!\\\’)\’)’)</code>
<pre>Print(‘Print(\’Print(\\\’helloworld!\\\’)\’)’)</pre>
其中\\就表示包含一个字符“\”的字符串变量,这样Print(‘\\’)就会打印出一个字符“\”,而Print(‘\\\’’)就会打印出字符串“\’”出来。所以,引号里面可以放入任意层次的引号。
其中\\就表示包含一个字符“\”的字符串变量,这样Print(‘\\’)就会打印出一个字符“\”,而Print(‘\\\’’)就会打印出字符串“\’”出来。所以,引号里面可以放入任意层次的引号。
但是这个程序仍然不能打印自己!你很快发现,我们人类是写不出这种能够打印自己的程序的,因为它包含了无穷递归。不过,通过蒯恩技巧,实际上我们完全可以写出来一个自打印程序,如下:
但是这个程序仍然不能打印自己!你很快发现,我们人类是写不出这种能够打印自己的程序的,因为它包含了无穷递归。不过,通过蒯恩技巧,实际上我们完全可以写出来一个自打印程序,如下:
<code>
<pre>
S(x){
S(x){
q=’S(x){\\nq=\\\’\’+q+\’\\\’;\\nPrint(\\\’\’+p(q)+\’\\\’);\\n}’;
q=’S(x){\\nq=\\\’\’+q+\’\\\’;\\nPrint(\\\’\’+p(q)+\’\\\’);\\n}’;
Print(‘S(x){\nq=\’’+q+’\’;\nPrint(\’’+p(q)+’\’);\n}’);
Print(‘S(x){\nq=\’’+q+’\’;\nPrint(\’’+p(q)+’\’);\n}’);
}
}
</code>
</pre>
<div style="text-align: center;">源代码1:自打印程序源代码</div>
<div style="text-align: center;">源代码1:自打印程序源代码</div>
这里面的“\n”表示换行符,即如果执行<code>Print(‘A\nB’)</code>,则程序会输出下面的字符串:
这里面的“\n”表示换行符,即如果执行<code>Print(‘A\nB’)</code>,则程序会输出下面的字符串:
<code>A</code>
<pre>
A
B
</pre>
“+”表示将两个字符串进行串联形成一个新的字符串,例如<code>A=’123’,B=’456’,则A+B=’123456’</code>。
“+”表示将两个字符串进行串联形成一个新的字符串,例如<code>A=’123’,B=’456’,则A+B=’123456’</code>。
如果我们把一个计算机程序<math>X</math>的描述(或者称源代码)写为<math>\lambda(X)</math>,则自打印程序的第一条赋值语句就相当于给<math>q</math>赋予了<math>\lambda (Copy_o \ Popup_o \ Control)</math>,即<math> (Copy_o \ Popup_o \ Control)</math>这三个程序连在一起的源代码。最后我们可以将自打印程序简写为:
如果我们把一个计算机程序<math>X</math>的描述(或者称源代码)写为<math>\lambda(X)</math>,则自打印程序的第一条赋值语句就相当于给<math>q</math>赋予了<math>\lambda (Copy_o \ Popup_o \ Control)</math>,即<math> (Copy_o \ Popup_o \ Control)</math>这三个程序连在一起的源代码。最后我们可以将自打印程序简写为:
<code>
<pre>
S(x){
S(x){
q= λ (Copy<sub>o</sub> Popup<sub>o</sub> Control)
q= λ (Copy<sub>o</sub> Popup<sub>o</sub> Control)
(Copy<sub>o</sub> Popup<sub>o</sub> Control)(q);
(Copy<sub>o</sub> Popup<sub>o</sub> Control)(q);
}
}
</code>
</pre>
<div style="text-align: center;">源代码2:自打印程序的源码缩写</div>
<div style="text-align: center;">源代码2:自打印程序的源码缩写</div>
那么我们只要这样修改S(x)就可以了:
那么我们只要这样修改S(x)就可以了:
<pre>
S(x){q=’S(x){\\nq=\\\’\’+q+\’\\\’;\\nF(\\\’\’+p(q)+\’\\\’);\\n}\\nF(x){\\nPrint(length(x));\\n}’;
S(x){q=’S(x){\\nq=\\\’\’+q+\’\\\’;\\nF(\\\’\’+p(q)+\’\\\’);\\n}\\nF(x){\\nPrint(length(x));\\n}’;
F(‘S(x){\nq=\’’+q+’\’;\nF(\’’+p(q)+’\’);\n}\nF(x){\nPrint(length(x));\n}’);
F(‘S(x){\nq=\’’+q+’\’;\nF(\’’+p(q)+’\’);\n}\nF(x){\nPrint(length(x));\n}’);
Print(length(x));
Print(length(x));
}
}
源代码4:计算自己代码长度的计算机程序
</pre>
<div style="text-align: center;">源代码4:计算自己代码长度的计算机程序</div>
注意,红色的代码部分就是在上一个代码的基础上添加的。这样,此程序不仅包含了S(x),而且还包含了一个附加的程序F(x)的定义,并且这个附加函数F(x)的源代码也需要被包含到之前F()之中和q的赋值语句之中。运行这个程序,它就会在屏幕上打印出自己源代码的长度。
注意,红色的代码部分就是在上一个代码的基础上添加的。这样,此程序不仅包含了S(x),而且还包含了一个附加的程序F(x)的定义,并且这个附加函数F(x)的源代码也需要被包含到之前F()之中和q的赋值语句之中。运行这个程序,它就会在屏幕上打印出自己源代码的长度。