zoukankan      html  css  js  c++  java
  • Qunie——自我生成程序

    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!
    ");
        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
    ", text[i]);
        }
        // printf("Hello, World!
    ");
        return 0;
    }

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

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

    必须想出——怎样输出第6行之后的这个string array的内容?回想一下,printf("%s ", 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
    ", 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
    ", 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
    ", 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
    ", 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
    ", text[i]);",
                    "       }",
                    "       for(i = 0; i < sizeof(text)/sizeof(text[0]); ++i) {",
    -               "               printf("               \"%s\",\n", text[i
    +               "               printf("                "%s",
    ", text[i]);",
                    "       }",
                    "       for(i = 6; i < sizeof(text)/sizeof(text[0]); ++i) {",
    -               "               printf("%s\n", text[i]);",
    +               "               printf("%s
    ", text[i]);",
                    "       }",
                    "       return 0;",
                    "}",

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

    更正

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

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

    对于第二处。有前后缀须要拼接,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)

  • 相关阅读:
    查看object信息
    Google C++单元测试框架之宏
    Google C++单元测试框架
    通过iscsi协议使用ceph rbd
    OpenStack+Ceph存储空间回收《转》
    IO
    golang之interface
    mysql 初始化
    ceph之ceph osd tree下的weight, reweight
    c++单元测试框架googletest
  • 原文地址:https://www.cnblogs.com/mfmdaoyou/p/6887841.html
Copyright © 2011-2022 走看看