打印自身的程序


当初看到matrix67上介绍的这个函数时震惊的不行,看了解释后发现其实也不是很难,于是最近又看到这个打印自身源码程序的程序,一开始大家都会有种神来之笔的感觉,其实了解到背后的想法和构思过程的话,其实自己去实现一个也不是很难的事情。

首先我们要说的这个问题其实已经是蛮常见的了,被成为Quine,详细的介绍可以看wiki,我们可以在这里看到很多quine程序,简单来说quine是表示一个可以生成他自己的完全的源代码的程序,但是要求程序不能接受输入,而且不能读文件,不然我们可以有如下简单的python程序。

print open(__file__).read()

因此我们这里需要无视掉类似读文件等cheating的做法,首先声明,我是看了这篇文章才理解的如何写quine的,如果英语不错的话,建议还是去读读原文。

对于c++的程序而言,我们都是这样写的

#include
int main(){
    //todo
    return 0;
}

于是我们的quine应该大体是这样的

#include
int main(){
    //todo
    printf("#include\nint main(){    //todo\n    printf();\n    retuen 0;\n}\n");
    return 0;
}

于是问题来了在printf里的printf写什么呢,又需要把整个程序写一遍吗,于是我们可以想到可以利用字符串来进行类似的操作也就是这样

a=”a=%s”;
printf(a,a);

那么,这就是我们quine的核心思想了,总结一下,就是说我们代码分为两部分,第一部分是data,第二部分是code,data中就是整个程序,描述了data自己还有code部分,然后code就是需要把data输出就可以了,而data中描述自己的技巧就是利用”%s”这样子了,于是c++代码如下。

#include
int main(){
    const char *a="#include%cint main(){%c    const char *a=%c%s%c;%c    printf(a,10,10,34,a,34,10,10,10,10);%c    return 0;%c}%c";
    printf(a,10,10,34,a,34,10,10,10,10);
    return 0;
}

还有个需要注意的地方就是引号和\n需要进行转义,这里就用了其ASCII中的值,34是引号,10就是\n。

ok,我们进一步做点更有难道的,让程序A输出程序B,程序B去生成程序A,先看我写的代码吧。

a='a=%s%cb=%s%cprint b%%(`b`,`a`)'
b='b=%s;a=%s;print a%%(`a`,10,`b`,10)'
print b%(`b`,`a`)
b='b=%s;a=%s;print a%%(`a`,10,`b`,10)';a='a=%s%cb=%s%cprint b%%(`b`,`a`)';print a%(`a`,10,`b`,10)

这两个都是python程序,其中一个可以去生成另一个。其实思想都是一样的每个程序都分为data和code两部分,然后让code去输出data部分,那么这里我们需要生成两个程序,所以每个程序都要有两个程序的描述,一个描述自己,另一个去描述另一个,然后code部分输出时依旧是利用那个技巧进行嵌套于是程序就出来了。

最后不得不提一下scturtle童鞋的这篇文章利用quine来实现不定义函数实现递归,也是很赞的东西阿。

您可能喜欢:
我猜您可能还喜欢:
,

有 1 条《打印自身的程序》的回复

  1. ltz | #1

    让程序A输出程序B,程序B去生成程序A ..
    令A=B, 自己打印自己,再编译自己。。哈哈

发表回复