Как пишутся сложные мультиквины?

Я определяю мультиквайн как:

Набор из n программ на n разных языках программирования, так что каждая из них, если не вводится, выводит свой точный исходный код, а когда вводится n в качестве входных данных выведите исходный код *n*й программы.

Это не следует путать с циклической последовательностью программ, где каждая программа выводит исходный код следующей, пока первая программа не будет выведена. В этом случае каждая программа не является квином, что противоречит сути дела. Эти циклические наборы, хотя и являются интересными головоломками для больших значений n, довольно просты в реализации.

Сложный в данном случае означает «для значений n больше или равных 2». Я считаю, что решение для n = 2 в данном случае достаточно сложное. Однако целью является общее решение (читай: стратегия) для всех значений n.

Я понимаю, как пишутся «простые» квины, однако я не могу понять сложные мультиквины, которые меня завораживают. Часть меня надеется, что нет никакого решения, кроме изобретательности в уме программиста, хотя я считаю это маловероятным.


person Swadq    schedule 12.12.2012    source источник
comment
Лучший способ получить ответ на этот вопрос — зайти на codegolf.SE и попросить людей попробовать написать мультиквайн. Установите критерии оценки (например, min(s/n), n ›= 2), где s — размер кода, и дождитесь ответов. :)   -  person Victor Stafusa    schedule 13.12.2012
comment
@Victor Хотя это правда, что это вероятно даст решение, меня не интересуют уродливые, короткие, запутанные мультикины. Меня даже не интересует решение, а скорее как создается решение. Вот почему я задал вопрос здесь, а не на codegolf, хотя должен признать, что ожидал немного большего ответа;)   -  person Swadq    schedule 13.12.2012


Ответы (1)


Нет ничего экстраординарного в квинах, многоязычных квинах, многоязычных куинах и так далее. Все они могут быть написаны в значительной степени автоматически.

Вот, например, болото-стандартный, многословный, неэлегантный, неэффективный квайн на C++. Однако, со всеми его недостатками, его довольно легко модифицировать, чтобы он делал то, что мы хотим.

#include <iostream>
#include <string>
#include <cstdlib>

std::string show (const std::string& in) {
    std::string res = "\"";
    for (std::string::const_iterator it = in.begin(); it < in.end(); ++it) {
        switch (*it) {
            case '"':
            case '\\':
                res += '\\';
            default:
                res += *it;
        }
    }
    res += "\"";
    return res;
}

int main (int argc, char* argv[])
{
    std::string arr[] = { // beginning ends here
"#include <iostream>",
"#include <string>",
"#include <cstdlib>",
"",
"std::string show (const std::string& in) {",
"    std::string res = \"\\\"\";",
"    for (std::string::const_iterator it = in.begin(); it < in.end(); ++it) {",
"        switch (*it) {",
"            case '\"':",
"            case '\\\\':",
"                res += '\\\\';",
"            default:",
"                res += *it;",
"        }",
"    }",
"    res += \"\\\"\";",
"    return res;",
"}",
"",
"int main (int argc, char* argv[])",
"{",
"    std::string arr[] = { // beginning ends here",
"======",
"    };",
"    int n = argc == 1 ? 0 : std::atoi(argv[1]);",
"    if (n == 0) {",
"        int i, j;",
"        for (i = 0; arr[i] != \"======\"; ++i) std::cout << arr[i] << std::endl;",
"        for (j = 0; j < sizeof(arr)/sizeof(arr[0]); ++j) std::cout << show(arr[j]) << ',' << std::endl;",
"        for (++i; i < sizeof(arr)/sizeof(arr[0]); ++i) std::cout << arr[i] << std::endl;",
"    } else {",
"    }",
"}",
    };
    int n = argc == 1 ? 0 : std::atoi(argv[1]);
    if (n == 0) {
        int i, j;
        for (i = 0; arr[i] != "======"; ++i) std::cout << arr[i] << std::endl;
        for (j = 0; j < sizeof(arr)/sizeof(arr[0]); ++j) std::cout << show(arr[j]) << ',' << std::endl;
        for (++i; i < sizeof(arr)/sizeof(arr[0]); ++i) std::cout << arr[i] << std::endl;
    } else {
    }
}

Как видите, сердцем программы является небольшая функция show, которая принимает строку и возвращает ее представление в виде литерала C++. Общая структура выглядит следующим образом: вывести начальную часть массива строк; вывести весь массив, переданный через show; напечатать конечную часть массива. Строковый массив — это копия программы, вставленная в середину программы. Начальная часть отделена от конечной специальной строкой "=====", которая не копируется из программы (она печатается только один раз, через show).

Легко вставить любые дополнительные действия, например, распечатать еще один квайн на другом языке. Я вставил заполнитель для такого действия.

Теперь это абсолютно тривиально перевести на любой язык программирования (например, FORTRAN). Допустим, мы его сделали, и он составлен из строк L1, L2, ..., LN. Мы вставляем эти операторы в местозаполнитель:

std::cout << "L1" << std::endl;
std::cout << "L2" << std::endl;
...
std::cout << "LN" << std::endl;

Мы соответствующим образом модифицируем массив строк. Вуаля, у нас есть quine, который может печатать сам себя, а также quine на FORTRAN, в зависимости от аргумента командной строки.

Хорошо, а как насчет фортрановского файла? Он может печатать только себя, а не C++ quine. Нет проблем, давайте скопируем quine C++ обратно в quine FORTRAN.

Но квайн C++ уже содержит весь квайн FORTRAN, дважды?

Нет проблем, так как Quine FORTRAN уже может печатать себя. Таким образом, нам нужно только скопировать исходные строки C++ обратно в FORTRAN. Нет необходимости лишний раз (или дважды) дублировать ФОРТРАН внутри себя.

Нам нужно только немного изменить FORTRAN. Когда мы просим quine FORTRAN напечатать quine C++, он должен напечатать все строки C++, а также все строки FORTRAN, дважды: один раз как Li и один раз как std::cout << "Li" << std::endl;, точно так же, как это есть в C++ quine. Затем мы получаем quine C++ (включая quine FORTRAN) обратно.

Нам также нужно перенести эти модификации FORTRAN обратно в C++ (то есть изменить std::cout << "Li" << std::endl; строк). И на этом волна модификаций останавливается.

Все, у нас есть две программы, которые могут печатать либо себя, либо друг друга, в зависимости от аргумента командной строки.

Я призываю вас сделать все это на самом деле.

person n. 1.8e9-where's-my-share m.    schedule 14.12.2012