Нет ничего экстраординарного в квинах, многоязычных квинах, многоязычных куинах и так далее. Все они могут быть написаны в значительной степени автоматически.
Вот, например, болото-стандартный, многословный, неэлегантный, неэффективный квайн на 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