Ответ 2
Здесь я написал, что использует putchar
вместо printf
; таким образом, он должен обрабатывать все свои собственные коды эвакуации. Но это 100% переносимо для всех наборов символов выполнения C.
Вы должны уметь видеть, что в текстовом представлении есть шов, который отражает шов в самом тексте программы, где он изменяется от работы в начале до работы в конце. Трюк с написанием "Куайна" преодолевает этот "горб", где вы переключаетесь, чтобы выкапывать свой путь из отверстия! Ваши параметры ограничены текстовым представлением и возможностями вывода языка.
#include <stdio.h>
void with(char *s) {
for (; *s; s++) switch (*s) {
case '\n': putchar('\\'); putchar('n'); break;
case '\\': putchar('\\'); putchar('\\'); break;
case '\"': putchar('\\'); putchar('\"'); break;
default: putchar(*s);
}
}
void out(char *s) { for (; *s; s++) putchar(*s); }
int main() {
char *a[] = {
"#include <stdio.h>\n\n",
"void with(char *s) {\n",
" for (; *s; s++) switch (*s) {\n",
" case '\\",
"n': putchar('\\\\'); putchar('n'); break;\n",
" case '\\\\': putchar('\\\\'); putchar('\\\\'); break;\n",
" case '\\\"': putchar('\\\\'); putchar('\\\"'); break;\n",
" default: putchar(*s);\n",
" }\n}\n",
"void out(char *s) { for (; *s; s++) putchar(*s); }\n",
"int main() {\n",
" char *a[] = {\n",
NULL }, *b[] = {
"NULL }, **p;\n",
" for (p = a; *p; p++) out(*p);\n",
" for (p = a; *p; p++) {\n",
" putchar('\\\"');\n",
" with(*p);\n",
" putchar('\\\"'); putchar(','); putchar('\\",
"n');\n",
" }\n",
" out(\"NULL }, *b[] = {\\",
"n\");\n",
" for (p = b; *p; p++) {\n",
" putchar('\\\"');\n",
" with(*p);\n",
" putchar('\\\"'); putchar(','); putchar('\\",
"n');\n",
" }\n",
" for (p = b; *p; p++) out(*p);\n",
" return 0;\n",
"}\n",
NULL }, **p;
for (p = a; *p; p++) out(*p);
for (p = a; *p; p++) {
putchar('\"');
with(*p);
putchar('\"'); putchar(','); putchar('\n');
}
out("NULL }, *b[] = {\n");
for (p = b; *p; p++) {
putchar('\"');
with(*p);
putchar('\"'); putchar(','); putchar('\n');
}
for (p = b; *p; p++) out(*p);
return 0;
}
Общей трюкой является переход на начало курса путем написания программы для чтения текстового файла и вывода массива чисел. Затем вы изменяете его для использования статического массива и запускаете первую программу против новой (статической) программы, создавая массив чисел, представляющий программу. Вставьте это в статический массив, запустите его снова до тех пор, пока он не опустится, и вы получите quine. Но он привязан к определенному набору символов (== не 100% портативный). Программа, подобная описанной выше (а не классический хакерский шрифт), будет работать одинаково на ASCII или EBCDIC (классический взломщик printf не работает в EBCDIC, поскольку содержит жестко закодированный ASCII).
изменить:
Снова прочитав вопрос, тщательно (наконец), похоже, вы на самом деле ищете больше философии, чем технику. Трюк, который позволяет вам выйти из бесконечного регресса, - это two-fer. Вы должны получить как закодированную программу, так и расширенную программу из тех же данных: используя те же данные двумя способами. Таким образом, эти данные описывают только часть программы, окружающую ее будущую манифестацию, кадр. Изображение в этом кадре является прямой копией оригинала.
Таким образом, вы, естественно, собираетесь производить рекурсивный рисунок вручную: телевизор телевизора. В какой-то момент вы устанете и просто нарисуете некоторый взгляд на экран, потому что рекурсия была достаточно установлена.
изменить:
Я ищу отличное объяснение того, почему Quines возможны.
"Возможность" Куайна входит в глубины математических революций XIX и XX веков. "Классический" quine W. V. O. Quine, представляет собой последовательность слов (IIRC)
дает false при добавлении к себе
который является парадоксальным, похожим на просьбу Дэвида о том, что "делает меня счастливым, когда грустно, и меня огорчает, когда он счастлив", ответил медальон, написанный с обеих сторон: "это тоже пройдет".
Такой же узел исследовали пионеры современной математической логики, такие как Фреге, Рассел и Уайтхед, Лукасевич и, конечно же, наши мальчики Тьюринг, Церковь и Туэ. Трюк, позволяющий перенести Куайн из сферы игрового процесса на программную демонстрацию (раскручивая парадоксальную часть на этом пути), был методом Гёделя для кодирования арифметических операций как чисел, поэтому полное математическое выражение может быть закодировано в одно (огромное) целое число. В частности, математическая функция, которая выполняет декодирование этого представления, может быть выражена в той же (числовой) форме. Это число (функция, кодированная Гёделем) - это код и данные.
Это силовое трио (код, представление, данные) может быть перенесено на разные репрезентации. Выбирая другое представление (или цепочку вроде: bytes- > ASCII- > hexadecimal- > integer), изменяет поведение кода, что изменяет внешний вид Data.