更改
→建构性的自指
虽然提起自指,人们很容易想到自指悖轮。但是,人们不熟悉的是另外一大类无害的自指,它们通常直接应用蒯恩技术,而实现某些意想不到的结构或者功能。下面,让我们先从一种最简单的建构性自指:自打印程序说起。
虽然提起自指,人们很容易想到自指悖轮。但是,人们不熟悉的是另外一大类无害的自指,它们通常直接应用蒯恩技术,而实现某些意想不到的结构或者功能。下面,让我们先从一种最简单的建构性自指:自打印程序说起。
所谓的程序自打印就是指一个程序能够在'''不读取外部文件'''的条件下把自己的源代码打印出来。首先,我们要先领教一下,一个自我打印的程序是多么不可能的!我们知道,要写一个程序打印出“helloworld!”字样是非常容易的,例如:
Print(‘helloworld!’)
<code>Print(‘helloworld!’)</code>
注意在这个程序中,字符串都用单引号括起来。那么,我们能不能写一个程序,把这个打印“helloworld!”程序的源代码打印出来呢?这也是可以办到的,例如下面的程序:
注意在这个程序中,字符串都用单引号括起来。那么,我们能不能写一个程序,把这个打印“helloworld!”程序的源代码打印出来呢?这也是可以办到的,例如下面的程序:
Print(‘Print(\’helloworld!\’)’)
<code>Print(‘Print(\’helloworld!\’)’)</code>
注意,这里面的“\’”会被编译器解释为一个字符串,这个字符串中就有一个字符:“'”。采用这个技巧,我们就可以解决如何在一个引号之中再输入一个引号的问题了。所以,我们可以很轻松地打印出这个能够打印”helloworld!”程序的程序源代码出来。但是很显然这个程序并不能打印出它自己,也许你会想到能不能打印出上面的程序源代码出来?当然可以!
注意,这里面的“\’”会被编译器解释为一个字符串,这个字符串中就有一个字符:“'”。采用这个技巧,我们就可以解决如何在一个引号之中再输入一个引号的问题了。所以,我们可以很轻松地打印出这个能够打印”helloworld!”程序的程序源代码出来。但是很显然这个程序并不能打印出它自己,也许你会想到能不能打印出上面的程序源代码出来?当然可以!
Print(‘Print(\’Print(\\\’helloworld!\\\’)\’)’)
<code>Print(‘Print(\’Print(\\\’helloworld!\\\’)\’)’)</code>
其中\\就表示包含一个字符“\”的字符串变量,这样Print(‘\\’)就会打印出一个字符“\”,而Print(‘\\\’’)就会打印出字符串“\’”出来。所以,引号里面可以放入任意层次的引号。
其中\\就表示包含一个字符“\”的字符串变量,这样Print(‘\\’)就会打印出一个字符“\”,而Print(‘\\\’’)就会打印出字符串“\’”出来。所以,引号里面可以放入任意层次的引号。
但是这个程序仍然不能打印自己!你很快发现,我们人类是写不出这种能够打印自己的程序的,因为它包含了无穷递归。不过,通过蒯恩技巧,实际上我们完全可以写出来一个自打印程序,如下:
但是这个程序仍然不能打印自己!你很快发现,我们人类是写不出这种能够打印自己的程序的,因为它包含了无穷递归。不过,通过蒯恩技巧,实际上我们完全可以写出来一个自打印程序,如下:
S(x){q=’S(x){\\nq=\\\’\’+q+\’\\\’;\\nPrint(\\\’\’+p(q)+\’\\\’);\\n}’;Print(‘S(x){\nq=\’’+q+’\’;\nPrint(\’’+p(q)+’\’);\n}’);
<code>
S(x){
q=’S(x){\\nq=\\\’\’+q+\’\\\’;\\nPrint(\\\’\’+p(q)+\’\\\’);\\n}’;
Print(‘S(x){\nq=\’’+q+’\’;\nPrint(\’’+p(q)+’\’);\n}’);
}
}
</code>
源代码1:自打印程序源代码
源代码1:自打印程序源代码