Что такое "трюк" для написания Куайна?

Я прочитал классическую статью Кен Томпсона Размышления о Trusting Trust, в которой он предлагает пользователям написать Quine как введение в его аргумент (рекомендуется прочитать).

Quine - это компьютерная программа, которая не принимает входных данных и создает копию собственного исходного кода в качестве единственного выхода.

Наивный подход состоит в том, чтобы просто сказать:

print "[insert this program source here]"

Но быстро видно, что это невозможно. Я закончил пишущий сам себя с помощью Python, но все еще испытываю трудности с объяснением "трюка". Я ищу отличное объяснение того, почему Quines возможны.

Ответы

Ответ 1

Обычным трюком является использование printf, чтобы строка формата представляла структуру программы, с владельцем места для самой строки для получения требуемой рекурсии:

Стандартный пример C из http://www.nyx.net/~gthompso/quine.htm иллюстрирует это довольно хорошо:

char*f="char*f=%c%s%c;main(){printf(f,34,f,34,10);}%c";main(){printf(f,34,f,34,10);}

edit: после написания этого я немного искал: http://www.madore.org/~david/computers/quine.html дает очень хорошее, более теоретические, описание того, что именно означает quines и почему они работают.

Ответ 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.