Куайн (от анг. quine) — программа, результатом работы которой является собственный исходный код. Сразу оговоримся: программы, которые обращаются к файлам или производят считывание с клавиатуры куайнами не являются. Более серьезное ограничение: программы, которые могут напрямую получить доступ к своему исходному коду (средствами языка), также не являются куайнами.
Пример на Бейсике:
10 LIST
Пример на Форте:
SOURCE TYPE
А существуют ли они?
Несмотря на простую формулировку задания, потратив немного времени на ее решение, возникает вопрос: существуют ли вообще такие программы? Ответ: Да!
Более того, куайн существует в любом языке, способном выводить произвольную вычисляемую строку! Впервые эта идея была описана Полом Братли и Жаном Милло. А первым куайном считается программа, написанная на языке Atlas Autocode Хэмишем Дюаром.
А на современных языках?
Да пожалуйста:
JavaScript:
function f(){alert(f.toString()+"f();");}f();
Pascal:
program autobiografija (output);
var c : array[1..14] of string[60];
i : integer;
begin
c[ 1]:='program autobiografija (output); ';
c[ 2]:=' var c : array[1..14] of string[60]; ';
c[ 3]:=' i : integer; ';
c[ 4]:='begin ';
c[ 5]:='for i := 1 to 4 do writeln(c[i]); ';
c[ 6]:='for i := 1 to 13 do writeln(c[14,1],c[14,2],i:2,c[14,5], ';
c[ 7]:=' c[14,6],c[14,7],c[14,8],c[i],c[14,8],c[14,9]); ';
c[ 8]:='for i := 1 to 8 do write(c[14,i]); ';
c[ 9]:='for i := 1 to 8 do write(c[14,i]); ';
c[10]:='for i := 8 to 60 do write(c[14,i]); ';
c[11]:='writeln(c[14,8],c[14,9]); ';
c[12]:='for i := 5 to 13 do writeln(c[i]); ';
c[13]:='end. ';
c[14]:='c[14]:=''; ';
for i := 1 to 4 do writeln(c[i]);
for i := 1 to 13 do writeln(c[14,1],c[14,2],i:2,c[14,5],
c[14,6],c[14,7],c[14,8],c[i],c[14,8],c[14,9]);
for i := 1 to 8 do write(c[14,i]);
for i := 1 to 8 do write(c[14,i]);
for i := 8 to 60 do write(c[14,i]);
writeln(c[14,8],c[14,9]);
for i := 5 to 13 do writeln(c[i]);
end.
Итак. Мы убедились, что такие программы существуют, а теперь немного теории о том, как их сделать.
Грабли
Интуитивно понятно, что нужно вывести значение переменной, в которой хранится частичный код программы. Почему частичный? Потому что само присваивание переменной тоже должно оказаться в значении переменной. Иными словами, значение переменной должно копировать само себя, из-за чего возникает бесконечная рекурсия. Неприятный момент.
Чтобы исправить положение, вообще не будем вносить в переменную сам факт присваивания. То есть:
char c[]="char c[]=;";
После, во время вывода подставим значение с в её же определение. Хорошо, но возникает проблема с кавычками. Языки, в которых определены одинарные и двойные кавычки, справляются с проблемой хорошо (мы говорим о том, что можно создать переменную q=” ‘ “, а потом вывести ее значение в ее определение), но что делать, например, с языком С? Экранирование, очевидно, не поможет, так как его тоже надо экранировать… В этом случае, можно задать кавычки кодом символа и вывести его.
Теперь остается проблема вставки строки в выходную строку с. Здесь вспомним про printf и всю его мощь.
printf("%.2s", с+1);
Чаще всего методы взятия подстроки умеют брать её до конца:
printf("%s", с+1);
Практика
Мы не будем лишать вас удовольствия написать собственный куайн, но покажем парочку экзотических примеров.
SELECT REPLACE(REPLACE('SELECT REPLACE(REPLACE("$",CHAR(34),CHAR(39)),CHAR(36),"$") AS Quine',CHAR(34),CHAR(39)),CHAR(36),'SELECT REPLACE(REPLACE("$",CHAR(34),CHAR(39)),CHAR(36),"$") AS Quine') AS Quine
Куайн, выводящий собственный код, это, конечно, хорошо. Но как вам идея куайна, который пишет другой код, являющийся куайном. Причем результатом работы последнего является первоначальный куайн! Заманчиво, не так ли?
Такого рода программы называются цепные куайны, и на сегодняшний день, самый большой цепной куайн (длина цепи уже достигла 100 языков) написан японцем Юсукэ Эндо.
Искусственный интеллект продолжает показывать себя как отличный инструмент для хакинга и взлома. На этот раз его использовали для перехвата изображения с монитора