среда, 21 ноября 2007 г.

Квайн или indirect self-reference

Информацию о QUINE вы можете почерпнуть из Wikipedia. Здесь, исключительно для удобства, я продублирую часть материала, немного его расширив и, видимо, откорректировав в некоторых моментах. Так же приведу свое решение для языка Java.
  1. История;
  2. Постановка задачи;
  3. Решение.
1. История
Куайн, квайн (quine [kwi:n]) - программа, воспроизводящая (печатающая) сама себя (свой исходный код).

Профессор Гарвардского университета, Уиллард Ван Орман Куайн (Willard Van Orman Quine) - философ и логик. Одной из изучаемых областей было косвенное самоупоминание (indirect self-reference). В связи с чем, видимо, и было присвоенно его имя данному типу задач в программировании.

2. Постановка задачи:
  1. Программа должна печатать свой исходный код;
  2. Внешние данные (чтение текста из файла, клавитуры, ...) использовать воспрещается;
  3. Программа, не содержащая исходного кода, квайном не является. (Вырожденный случай);


  4. Сама по себе задача не является сложной. Поэтому, мы добавили еще один пункт.
  5. Должна содержать наименьшее возможное количество байт исходного кода.

3. Решение

В момент, когда я приступал к реализации программы, мне ничего не было известно о "Квайнах". Мождно было воспользоваться "google.com" но это не спортивно и не интересно.

Решение задачи напрямую зависит от языка программирования и предоставляемых им средств. Так, например, для языка Basic, самой короткой программой будет "10 LIST".

Нас же интересует Java, и версия была выбрана 1.6. Задача класически делится на две части. Первая часть - это данные, собственно текст программы. Вторая же часть, механизм вывода.
Строка примерно будет выглядеть так:
1 String programm = "class A {"      
2
"public static void main(String[] args) {" +
3
" String programm=<вся проблема в том, что будет здесь>;" +
4
" System.out.prinln(programm);" +
5
"}" +
6
"};"

Вот, половина программы готова. Остается только самое интересное в этой задаче: из 3ей строки очевидно, что без реккурсивной вставки нам не обойтись. Для этого есть несколько вариантов, но так как мы используем Java 1.6, то я выбираю java.util.Formatter и java.io.PrinteStream. В дальнейшем будет видно, что данный выбор является так же и самым экономичным для размера исходного кода.
1 String programm="class A { "+
2
" public static void main(String[] args){"+
3
" String programm=%c%s%1$c;"+
4
" System.out.printf(programm,34,programm);"+
5
" }"+
6
"}"

Полный текст выглядит так:
1class A {
2
public static void main(String[] args) {
3 String programm =
"class A { "+
5
" public static void main(String[] args){"+
6
" String programm=%c%s%1$c;"+
7
" System.out.printf(programm,34,programm);"+
8
" }"+
9
"}";
10 System.out.printf(programm,
34, programm);
11 }
12}
Уберем все лишнее и получим 172 байта:
class A{public static void main(String[]a){String b="class A{public static void main(String[]a){String b=%c%s%1$c;System.out.printf(b,34,b);}}";System.out.printf(b,34,b);}}
При получении данного варианта, возникает желание уменьшить программу, хоть как нибудь. Кто то возможно скажет, что куда уже меньше, для Java и так хороший результат.
Но хороший - это не лучший, мы ведь стремимся к вечному и красивому.

Конструкция "public static void main(String[]a)" очень громоздкая, и справедливо хочется избавиться от нее. Что я и сделал подменой на static конструкцию.
Код был переписан к 116 байтам:
class B{static{String b="class B{static{String b=%c%s%1$c;System.out.printf(b,34,b);}}";System.out.printf(b,34,b);}}

Программа компилируется и работает. Для запуска рекомендую разделить output. Сделать вы это можете так:
"java B 1>std.out 2>err.out 3>wrn.out".
std.out - будет содержать исходный код программы.
err.out - java.lang.NoSuchMethodError: main
Exception in thread "main"
wrn.out - будет пустым.
Авторами данного кода являются: Максимом Прохоренко и Константином Вырским.

Среди знакомых были неплохие попытки читерства на 176 байт :)
class P{public static void main(String[]a)throws Exception{java.io.InputStream in=new java.io.FileInputStream("P.java");int b;while((b=in.read())>0)System.out.print((char)b);}}

А какой результат, получился у вас? Делитесь в коментариях или пишите письма. Так же буду рад, если вы укажете на мои не точности или поправите меня.

P.S.
Недавно, в руки мне попала, книга "Алгоритмические трюки для программистов." (Генри Уоррен, мл. ISBN:5-8459-0471-4 рус.)
Где я вычитал следующее: "Самая короткая программа на C, известная автору книги, написанна Владом Таировым и Рашидом Фахреевым и содержит 64 символа:"
main(a){printf(a,34,a="main(a){printf(a,34,a=%c%s%с,34);}",34);)}

P.P.S. Огромное множество, примеров, для разных языков вы сможете найти на сайте Гарри Томпсона.

Комментариев нет: