Techniki pisania quine'ów są różne. Ja zaczynam od dwóch zmiennych: x oraz y. Do pierwszej wpisuję kod tworzący te dwie zmienne, lecz bez zawartości. Do drugiej leci druga część programu, zaś w niej znajduje się kod, który pobiera wartość zmiennej x i wstawia w puste miejsca od końca: wartość zmiennej y, a później siebie samej, oczywiście obie escape'owane. Na końcu wyświetlam y. Quine jest gotowy. Oto implementacje w PHP oraz w D (przełamania linii dodane dla czytelności):
<?php $x = '<'.'?php $x = \'%\'; $y = \'^\'; '; $y = 'echo substr_replace(substr_replace(substr_replace($x, addslashes($y), 22, 1), addslashes($x), 12, 1).$y, \'\\\'.\\\'\', 13,0); ?>'; echo substr_replace(substr_replace(substr_replace($x, addslashes($y), 22, 1), addslashes($x), 12, 1).$y, '\'.\'', 13,0); ?>
import std.string; import std.stdio; char[] x = "import std.string; import std.stdio; char[] x = \"%s\"; char[] y = \"%s\"; "; char[] y = "char [] addslashes(char [] ppp){ char [] abc; int i; for(i = 0; i < ppp.length; i++){ switch(ppp[i]){ case '\\\"': abc ~= \"\\\\\\\"\"; break; case '\\\\': abc ~= \"\\\\\\\\\"; break; default: abc ~= ppp[i]; } } return abc; } int main(){ writef(x, addslashes(x), addslashes(y)); writefln(y); return 0; }"; char [] addslashes(char [] ppp){ char [] abc; int i; for(i = 0; i < ppp.length; i++){ switch(ppp[i]){ case '\"': abc ~= "\\\""; break; case '\\': abc ~= "\\\\"; break; default: abc ~= ppp[i]; } } return abc; } int main(){ writef(x, addslashes(x), addslashes(y)); writefln(y); return 0; }
W PHP wykorzystałem do umieszczenia wartości obu zmiennych w $x funkcję substr_replace() (str_replace() odpada, ponieważ nie wstawia wszystkich wartości w jednej iteracji i powstają błędy). W D skorzystałem natomiast ze składni sprintf() :), lecz tu musiałem pisać własną funkcję escape'ującą i stąd listing jest taki długi. Oczywiście białe znaki i ładne formatowanie usuwa się po ukończeniu programu :). Jak komuś się nudzi, może to sobie ładnie rozbić.
W sieci można znaleźć mnóstwo quine'ów napisanych w rozmaitych językach. Oto klasyczny wręcz przykład w języku C:
char*f="char*f=%c%s%c;main(){printf(f,34,f,34,10);}%c";main(){printf(f,34,f,34,10);}
Szczególnie warte uwagi są napisane w językach ezoterycznych, np. Brainfuck (łamanie linii dodane dla lepszej czytelności):
->++>+++>+>+>+++>>>>>>>>>>>>>>>>>>>>>>+>+>++>+++>++>>+++>+>>>>>>>>>>>>>>>>>>>>>> >>>>>>>>>>>+>+>>+++>>>>+++>>>+++>+>>>>>>>++>+++>+++>+>+++>+>>+++>>>+++>+>++>+++> >>+>+>+>+>++>+++>+>+>>+++>>>>>>>+>+>>>+>+>++>+++>+++>+>>+++>+++>+>+++>+>++>+++>+ +>>+>+>++>+++>+>+>>+++>>>+++>+>>>++>+++>+++>+>>+++>>>+++>+>+++>+>>+++>>+++>>+[[> >+[>]+>+[<]<-]>>[>]<+<+++[<]<<+]>+[>>]+++>+[+[<++++++++++++++++>-]<++++++++++.<]
Pragnącym zacząć jak najszybciej, polecam napisanie quine'a w języku HQ9+ :). Kod ma długość dokładnie jednego znaku.
Istnieją również odmiany idei quine'a. Iteracyjne quine'y to zbiór różnych programów, z których pierwszy generuje źródła drugiego, drugi trzeciego, itd. zaś n-ty - pierwszego. Można też bawić się w pisanie generatorów quine'ów. Wśród twórców języków programowania popularne jest też pisanie programów wyświetlających treść piosenki 99 butelek piwa. Swego czasu zabrałem się nawet za pisanie takowego dla Revomera, ale po trzech godzinach implementowania mnożenia DWÓCH liczb skapitulowałem - dopiero później wymyśliłem ciekawe sposoby na obejście "tradycyjnej" arytmetyki, operując bezpośrednio na znakach ASCII. Zaś quine w Revomerze... akutalnie nie potrafię sobie takowego wyobrazić :D.















Napisał m_gol w piątek, 7 września 2007 o 22:57
Chciałem sprawdzić ten php i wydaje się nie działać. Co mogę robić źle?