打印自身源代码的程序,这是
翻看《More Programming Pearls》时的一道作业题。
记得以前曾看到Ken Thompson老师的一个版本
http://www.cnblogs.com/coderzh/archive/2008/05/13/1194426.html 不过这个
版本严格来讲不是合格的的打印自身程序,虽然两段文本的编译结果相同,但用
diff程序比较的话,还是会报告差异的。
而且,区区其实也打算自己实现一个算法。
书时已经是深深夜,因为不想开电脑,就空想着整理出一个算法来:
程序文本中定义一个字符串常量self_str的编码结果,然后在主程序中循环解码
输出self_str的每个,当输出循环到特定偏移时,把未解码的字符串self_str输
出一次并继续循环。
如果用google仔细搜一下,还会有很多有趣的实现,比如在这里
(http://www.cnblogs.com/coderzh/archive/2008/05/13/1194445.html)
就有一个简洁的python实现,只用两行,相比于C版本很好很强大:
a = "print 'a = ', repr(a), '/n', repr(a)[1:-5]', a" print 'a = ', repr(a), '/n', repr(a)[1:-5]
而在这里的"打印自身的程序杂谈"――下称"打谈"
(http://notabdc.vip.sina.com/Program/printself.htm)
同样可以找到C语言只用两行代码的版本:
#include <stdio.h> int main() { char *s = "#include <stdio.h>%cint main() { char *s = %c%s%c; printf( s, 10, 34, s, 34 ); return 0; }"; printf( s, 10, 34, s, 34 ); return 0; }
是不是神乎其技呢,然而留意上文的你会发现,作者最终给出另一个版本,很长的版本,因为上述
两个twoliner都过分依赖于特定语言中的特性(也就是python的repr和C语言中的printf)。
这对于描述一个通用的打印自身程序来说是不完备的。
"打谈"的通用算法基本思想是:
1.在程序中生成字符串self;
2.加入把self改写成程序自身的算法编码;
3.输出self字符串。
如此看来"打谈"的算法应该是一种内编码,结果必然是程序难以阅读。
区区实现的是外编码的技术,缺点是要写两个程序,优点呢:
1.程序很清析,没有让人觉得神秘的语言特性使用
2.外编码的程序可以复用,编写其它语言的版本会很简单
文件1:self.cxx (注意到self_str先写为空值,里面要写什么,由make_self.cxx程序计算)
const char *self_str=""; #include<map> #include<string> #include<iostream> #include<sstream> #include<iomanip> #include<fstream> using namespace std; string enc(const string &s){ stringstream ss; if (s.size()==1){ int i = s[0]; ss<<setw(2)<<setfill('0')<<setbase(16)<<i; return ss.str(); } else { istringstream ins(s); int c; ins>>setw(2)>>setbase(16)>>c; ss<<static_cast<char>(c); return ss.str(); } } int main(){ string s = self_str; int prefix_len = 2 * strlen("const char *self_str="); for (int i=0; i< s.size(); i+=2){ cout<<enc(s.substr(i,2)); if (i==prefix_len) cout<<s; } return 0; }
文件2:make_self.cxx (编译并运行此程序,会得到一个字符串,将其填入到self.cxx首行的引号内)