Qunie是一段没有输入。但输出和它本身源代码同样的程序。本文无不论什么高深技术,纯属娱乐!

近期看到wikipedia的一个词条——Quine,简单介绍部分摘录于此,并简要翻译:

A quine is a non-empty computer program which takes no input and produces a copy of its own source code as its only output. The standard terms for these programs in the computability theory and computer science literature are “self-replicating programs”, “self-reproducing programs”, and “self-copying programs”.

Qunie是一段计算机程序。它没有输入,但输出和它本身的源代码同样。在可计算理论和计算机科学领域的标准定义,叫做“自我反复程序”、“自我生成程序”和“自我拷贝程序”。

从上面这段简单介绍中能够看到,Qunie是一段没有输入。但输出和它本身源代码同样的程序。

从hello world開始

经典的Hello world程序例如以下:

#include <stdio.h>

int main()
{
printf("Hello, World!\n");
return 0;
}

如今。就从这个程序開始,常识写出Quine。输出源代码文本,又有输入。仅仅能用hard-coding一个string array了;一旦開始尝试。你会发现,string array写到以下的时候,写不下去了:

#include <stdio.h>

int main()
{
int i;
char* text[] = {
"#include <stdio.h>",
"",
"int main()",
"{",
" char* text[] = {",
// ...
}; for(i = 0; i < sizeof(text)/sizeof(text[0]); ++i) {
printf("%s\n", text[i]);
}
// printf("Hello, World!\n");
return 0;
}

不能继续反复。否则就是“无限递归”,string array就写不完了。如今,仅仅能输出到第6行。

“山里有个庙”的故事是讲不完的

必须想出——怎样输出第6行之后的这个string array的内容?回想一下,printf("%s\n", text[i]);仅仅输出了引號内的内容,加上前缀(" \"", TAB键能够直接写在字符串里面)和后缀("\",")就可以,即printf(" \"%s\",", text[i]);

于是,能够继续写:

#include <stdio.h>

int main()
{
int i;
char* text[] = {
"#include <stdio.h>",
"",
"int main()",
"{",
" char* text[] = {",
// ...
}; for(i = 0; i < sizeof(text)/sizeof(text[0]); ++i) {
printf("%s\n", text[i]);
}
for(i = 0; i < sizeof(text)/sizeof(text[0]); ++i) {
printf(" \"%s\",", text[i]);
}
return 0;
}

这样。程序能够输出到第11行了。

第一个看似正确的解答

剩下的源代码也是要写到text里,同一时候要保证——string array的输出在两者中间,于是第一个循环也要被拆为两个部分:

#include <stdio.h>

int main()
{
int i;
char* text[] = {
"#include <stdio.h>",
"",
"int main()",
"{",
" char* text[] = {",
" };",
"",
" for(i = 0; i < sizeof(text)/sizeof(text[0]); ++i) {",
" printf("%s\n", text[i]);",
" }",
" for(i = 0; i < sizeof(text)/sizeof(text[0]); ++i) {",
" printf(" \"%s\",", text[i]);",
" }",
" return 0;"
"}",
}; for(i = 0; i < 6; ++i) {
printf("%s\n", text[i]);
}
for(i = 0; i < sizeof(text)/sizeof(text[0]); ++i) {
printf(" \"%s\",", text[i]);
}
for(i = 6; i < sizeof(text)/sizeof(text[0]); ++i) {
printf("%s\n", text[i]);
}
return 0;
}

用工具验证

这个版本号就是正确的么?细微的区别,眼睛看起来费劲,且文本比較本来就该是工具干的。

编译:

xusiwei1236@csdn.net:~/test$ gcc quine-printf.c

执行:

xusiwei1236@csdn.net:~/test$ ./a.out > out

比較:

xusiwei1236@csdn.net:~/test$ git diff quine-printf.c out

git diff一比,发现还是有点区别的:

diff --git a/quine-printf.c b/out
index ec3ecf9..d48d2ab 100644
--- a/quine-printf.c
+++ b/out
@@ -13,13 +13,13 @@ int main()
" };",
" ",
" for(i = 0; i < 6; ++i) {",
- " printf(\"%s\\n\", text[i]);",
+ " printf("%s\n", text[i]);",
" }",
" for(i = 0; i < sizeof(text)/sizeof(text[0]); ++i) {",
- " printf(\" \\\"%s\\\",\\n\", text[i
+ " printf(" \"%s\",\n", text[i]);",
" }",
" for(i = 6; i < sizeof(text)/sizeof(text[0]); ++i) {",
- " printf(\"%s\\n\", text[i]);",
+ " printf("%s\n", text[i]);",
" }",
" return 0;",
"}",

能够看到有三处差异,都是由于转义字符的问题;大体有两类,格式化字符串的开头和结尾的引號。以及前后缀字符串中间的特殊字符。

更正

直接用printf看起来像是没办法,由于不可避免的要用格式化字符串,自然就有引號。相应的字符串里面就会有转义字符。对于第一、第三处的printf("%s\n", text[i]);非常easy想到。能够用puts(text[i]);替换。既简单又优雅(对于的字符串也要更改,这里仅仅是对于的代码部分)的攻克了格式字符串的开头和结尾的引號(")和换行符(\n)。

这样攻克了格式化字符的问题。

对于第二处。有前后缀须要拼接,C的标准库的strcat提供了该功能(须要注意strcat要求左边的字符串有足够的空间)。于是能够将第二处改为:

        char prefix[128] = "        \"";
char postfix[] = ",";
puts(strcat(strcat(prefix, text[i]), postfix));

这样仍然没有解决前后缀中的特殊字符,看到[]。我们自然想到,能够直接写成员的值(别拿村长不当干部,别当字符数组不是数组,查一下ASCII码表):

        char prefix[128] = {0x09, 0x09, 0x22, 0}; // 0x09 table, 0x22 quote
char postfix[] = {0x22, 0x2C, 0}; // 0x22 quote, 0x2C comma
puts(strcat(strcat(prefix, text[i]), postfix));

好了,如今完美了(__LINE__是行号的提前定义宏。为了消除后面6那个magic number。让程序看起来更优雅):

#include <stdio.h>
#include <string.h> int main()
{
int i;
int header = __LINE__ + 1;
char* text[] = {
"#include <stdio.h>",
"#include <string.h>",
"",
"int main()",
"{",
" int i;",
" int header = __LINE__ + 1;",
" char* text[] = {",
" };",
" ",
" for(i = 0; i < header; ++i) {",
" puts(text[i]);",
" }",
" ",
" for(i = 0; i < sizeof(text)/sizeof(text[0]); ++i) {",
" char prefix[128] = {0x09, 0x09, 0x22, 0}; // 0x09 table",
" char postfix[] = {0x22, 0x2C, 0}; // 0x22 quote, 0x2C comma",
" puts(strcat(strcat(prefix, text[i]), postfix));",
" }",
" ",
" for(i = header; i < sizeof(text)/sizeof(text[0]); ++i) {",
" puts(text[i]);",
" }",
" ",
" return 0;",
"}",
}; for(i = 0; i < header; ++i) {
puts(text[i]);
} for(i = 0; i < sizeof(text)/sizeof(text[0]); ++i) {
char prefix[128] = {0x09, 0x09, 0x22, 0}; // 0x09 table
char postfix[] = {0x22, 0x2C, 0}; // 0x22 quote, 0x2C comma
puts(strcat(strcat(prefix, text[i]), postfix));
} for(i = header; i < sizeof(text)/sizeof(text[0]); ++i) {
puts(text[i]);
} return 0;
}

转载注明原文链接,及出处:http://blog.csdn.net/xusiwei1236

wikipedia的Qunie词条,能够看到很多其它其它语言版本号的Qunie:https://en.wikipedia.org/wiki/Quine_(computing)

Qunie——自我生成程序的更多相关文章

  1. 基于Python实现的四则运算生成程序

    Github项目地址:传送门 小组成员:黄晓彬(代码实现) 黄钰城(代码审查) 需求: 1. 使用 -n 参数控制生成题目的个数. 2. 使用 -r 参数控制题目中数值(自然数.真分数和真分数分母)的 ...

  2. 拨开迷雾,找回自我:DDD 应对具体业务场景,Domain Model 到底如何设计?

    写在前面 除了博文内容之外,和 netfocus 兄的讨论,也可以让你学到很多(至少我是这样),不要错过哦. 阅读目录: 迷雾森林 找回自我 开源地址 后记 毫无疑问,领域驱动设计的核心是领域模型,领 ...

  3. 《web全栈工程师的自我修养》读书笔记

    有幸读了yuguo<web全栈工程师的自我修养>,颇有收获,故在此对读到的内容加以整理,方便指导,同时再回顾一遍书中的内容. 概览 整本书叙述的是作者的成长经历,通过经验的分享,给新人或者 ...

  4. JavaScript之自我总结篇

    最近在看汤姆大叔的"深入理解JavaScript系列",写得真的不错,对于我而言特别是12章到19章,因为大叔研究的点,就主要是从底层来研究JavaScript为什么会出现钟种特有 ...

  5. WCF服务自我寄宿 Windows服务

    WCF寄宿有自我寄宿跟IIS寄宿 服务代码: [ServiceContract] ---服务契约 public interface ICustomerService { [OperationContr ...

  6. 20155229-付钰涵-分析自我技能延展到c语言学习状况

    我的小技能 我记得幼儿园时表演的舞蹈,也记得从水彩到素描的学习,还记得小学和初中获得的钢琴省级奖项. 舞蹈止于一年级,绘画止于三年级,钢琴从学前班到高一那十年的时间里有过断续. 03年-04年的那个冬 ...

  7. 转载:Scalers:要持续行动,不要自我感动

    转载自微信公众号:http://mp.weixin.qq.com/s?__biz=MzA4MjIyNDYzMQ==&mid=2650846277&idx=1&sn=5d832a ...

  8. <转>exe & dll自我更新

    exe & dll自我更新 分类: c/c++ 2008-10-16 22:07 756人阅读 评论(1) 收藏 举报 exedlldelayapi游戏 exe与dll的自我更新     在改 ...

  9. 敏捷个人-认识自我,管理自我 v0.8.pdf 下载

    2009年我在blog上写了个人管理系列的一些blog,其中一些文章深受大家的喜欢.想到写这个系列是源于在实施敏捷Scrum方法时,对方法实施是否对人的水平需要高要求的一些思考.自组织团队是建立在敏捷 ...

随机推荐

  1. luogu2754 星际转移问题 网络流

    题目大意:地球与月球间有可容纳无限人的太空站,还有在太空站与星球间按周期行驶的.有固定容量的太空船,每一艘太空船从一个太空站驶往任一太空站耗时均为 1.地球上有一定数量的人,问所有人到月球最少需要多少 ...

  2. Ubuntu Tomcat Service

    只需要将%TOMCAT_HOME%/bin/catalina.sh文件拷贝到/etc/init.d/文件夹下,稍作编辑,然后注册成系统服务,是否设置自启动均可. 1. 编辑catalina.sh文件c ...

  3. 关于ShapeDrawable应用的一些介绍(下)

    我们今天接着来看一下Shape中的stroke属性,stroke其实就是边缘的意思,当我们在定义画笔的时候,有很多时候会用到 FILL 和 STROKE,前者能够画出一个实心的形状,而后者就画出一个空 ...

  4. EOJ 2847 路由结点

    数学知识 凸N边形的对角线条数为:n(n-3)/2因为每一个交点对应两条对角线,而两条对角线又对应着一个四边形.于是焦点个数就对应四边形的个数.问题转化成由凸n边形的n个顶点取4个顶点可组成多少个四边 ...

  5. Oracle 11g RAC for LINUX rhel 6.X silent install(静默安装)

    一.前期规划 1.硬件环境 CPU: Intel(R) Xeon(R) CPU E7-4820 v4 @ 2.00GHz  8*10核 内存:512GB OCR:2147*5 MB DATA1:2TB ...

  6. Web启动,停止Windows服务

    When you grow stronger,the world become more dangerous.当你变得越强大,这个世界反而会变得越危险. ServiceModel.cs代码: publ ...

  7. 错误信息:getOutputStream() has already been called for this response

    原因(转): getOutputStream()和getWriter()这两个方法不能在一个请求内同时使用, 如果使用forward,这时将要跳转到的页面是要用getWriter()方法获得输出流把页 ...

  8. sql server<> != 从数据类型varchar转换为numeric 时出错

    sql server 数据类型转换出错 字段percentcomplete 是 varchar 类型, 都是存的数字 用 where cast(percentcomplete as numeric(1 ...

  9. RabbitMQ学习之集群模式

    由于RabbitMQ是用erlang开发的,RabbitMQ完全依赖Erlang的Cluster,因为erlang天生就是一门分布式语言,集群非常方便,但其本身并不支持负载均衡.Erlang的集群中各 ...

  10. (转)RabbitMQ学习之spring整合发送同步消息

    http://blog.csdn.net/zhu_tianwei/article/details/40890543 以下实现使用Exchange类型为DirectExchange. routingke ...