Quine (program komputerowy)
Quine – program komputerowy, rodzaj metaprogramu, którego jedynym celem jest wypisanie własnego kodu źródłowego na wyjściu. Także plik skompresowany, który dekompresuje się do siebie samego. Częstą zabawą programistów jest pisanie najkrótszych quine’ów w danym języku programowania.
Należy zauważyć, że programy, które otwierają własny kod źródłowy i wypisują go na wyjściu (tak jak pierwszy przykład w języku BASIC poniżej), są uważane za nieuczciwe. Także quine, który nie zawiera żadnej zawartości, jest wykluczany.
Nazwa quine pochodzi od nazwiska filozofa Willarda Van Ormana Quine’a, który zajmował się m.in. pośrednią autoreferencją.
Przykłady prostych quine’ów w różnych językach programowania
[edytuj | edytuj kod]10 LIST
Powyższy przykład może być uważany za oszukiwanie, z powodu bezpośredniego dostępu do kodu, poniżej jest bardziej skomplikowany:
10 C=": PRINT CHR(49)+CHR(48)+CHR(32)+CHR(67)+CHR(61)+CHR(34)+C+CHR(34)+C":
PRINT CHR(49)+CHR(48)+CHR(32)+CHR(67)+CHR(61)+CHR(34)+C+CHR(34)+C
<>>#"652**:,2+:,,75*,89+2*,>:#,_89+2*,@"
(Kod powinien być napisany w jednym wierszu, ale w celu zwiększenia czytelności wprowadzono łamanie linii.)
->++>+++>+>+>+++>>>>>>>>>>>>>>>>>>>>>>+>+>++>+++>++>>+++>+>>>>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>+>+>>+++>>>>+++>>>+++>+>>>>>>>++>+++>+++>+>+++>+>>+++>>>+++>+>++>+++>
>>+>+>+>+>++>+++>+>+>>+++>>>>>>>+>+>>>+>+>++>+++>+++>+>>+++>+++>+>+++>+>++>+++>+
+>>+>+>++>+++>+>+>>+++>>>+++>+>>>++>+++>+++>+>>+++>>>+++>+>+++>+>>+++>>+++>>+[[>
>+[>]+>+[<]<-]>>[>]<+<+++[<]<<+]>+[>>]+++>+[+[<++++++++++++++++>-]<++++++++++.<]
C
[edytuj | edytuj kod]#include<stdio.h>
char*i="\\#include<stdio.h>",n='\n',q='"',*p=
"%s%cchar*i=%c%c%s%c,n='%cn',q='%c',*p=%c%c%s%c,*m=%c%c%s%c%c;%s%c",*m=
"int main(){return!printf(p,i+1,n,q,*i,i,q,*i,q,n,q,p,q,n,q,m,q,n,m,n);}"
;int main(){return!printf(p,i+1,n,q,*i,i,q,*i,q,n,q,p,q,n,q,m,q,n,m,n);}
Inny przykład (kod powinien być w jednej linii, przy założeniu, że kompilator działa z ASCII:
extern printf(char*,...);main(){char*a="extern printf(char*,...);
main(){char*a=%c%s%c;printf(a,34,a,34,10);}%c";printf(a,34,a,34,10);}
albo nawet krócej (chociaż nie jest to zgodne z ISO C99):
main(){char*a="main(){char*a=%c%s%c;printf(a,34,a,34);}";printf(a,34,a,34);}
Ten przykład nie zależy od ASCII i używa preprocesora:
#define T(a) main(){printf(a,#a);}
T("#define T(a) main(){printf(a,#a);}\nT(%s)\n")
Należy zauważyć, że programy o zerowej długości też są uważane za quine tylko wtedy, gdy można je skompilować do pliku wykonywalnego, który nic nie robi (nie wypisuje znaków na stdout). Jest możliwość uzyskania takiego efektu w C poprzez sztuczkę w makefile (ponieważ standardowo nie stworzy on pliku wykonywalnego). Ten trik wygrał nagrodę za najgorsze nadużycie zasad w konkursie dla najbardziej zaciemnionego kodu w C.
C++
[edytuj | edytuj kod](dodane łamanie linii dla lepszej czytelności)
#include <iostream>
int main(){const char c=',',dq='"',q[]="'",*s[]={"#include <iostream>",
"int main(){const char c=',',dq='","',q[]=",",*s[]={","};std::cout<<s[0]<<std::endl<<s[1]<<dq<<s[2]
<<dq<<q<<dq<<s[3]<<dq<<s[0]<<dq<<c<<dq<<s[1]<<dq<<c<<dq<<s[2]<<dq<<c<<dq<<s[3]<<dq<<c<<dq<<s[4]<<dq
<<s[4]<<std::endl;}"};std::cout<<s[0]<<std::endl<<s[1]<<dq<<s[2]<<dq<<q<<dq<<s[3]<<dq<<s[0]<<dq<<c
<<dq<<s[1]<<dq<<c<<dq<<s[2]<<dq<<c<<dq<<s[3]<<dq<<c<<dq<<s[4]<<dq<<s[4]<<std::endl;}
C#
[edytuj | edytuj kod](dodane łamanie linii dla lepszej czytelności)
using System;
namespace quine
{
class Program
{
[STAThread]
static void Main(string[] args)
{
string s = "using System;{0}namespace quine{0}{2}{0}{1}class Program{0}
{1}{2}{0}{1}{1}[STAThread]{0}{1}{1}static void Main(string[] args){0}{1}{1}{2}{0}{1}{1}{1}
string s = {4}{6}{4};{0}{1}{1}{1}Console.Write(s, Environment.NewLine, {4}{5}t{4}, {4}{2}
{4}, {4}{3}{4}, {4}{5}{4}{4}, {4}{5}{5}{4}, s);{0}{1}{1}{3}{0}{1}{3}{0}{3}";
Console.Write(s, Environment.NewLine, "\t", "{", "}", "\"", "\\", s);
}
}
}
Prostsza wersja z uwzględnionym łamaniem linii:
class Q
{
static void Main()
{
string s = @"class Q
{0}
static void Main()
{0}
string s = @{2}{3}{2};
System.Console.Write(s, '{0}', '{1}', '{2}', s);
{1}
{1}";
System.Console.Write(s, '{', '}', '"', s);
}
}
Usunięte łamanie linii:
class Q{static void Main(){string s="class Q{0}static void
Main(){0}string s={2}{3}{2};System.Console.Write(s,'{0}',
'{1}','{2}',s);{1}{1}";System.Console.Write(s,'{','}','"',s);}}
Dc
[edytuj | edytuj kod][91PP[dx]93PP]dx
DOS Batch
[edytuj | edytuj kod] @echo off
%1 %2
call %0 goto e %%
call %0 goto e %%3 echo.%%4
echo :f
goto f
:e
echo.%4@echo off
echo.%4%31 %32
echo.%4call %30 goto e %3%3
echo.%4call %30 goto e %3%33 echo.%3%34
echo.%4echo :f
echo.%4goto f
echo.%4:e
:f
Inny:
more %0.bat
Lub jeszcze inny:
type %0
(fun(S) -> io:format("(~s) (~p).", [S, S]) end) ("fun(S) -> io:format(\"(~s) (~p).\", [S, S]) end").
HQ9+
[edytuj | edytuj kod]Q
Java
[edytuj | edytuj kod](W rzeczywistym kodzie nie ma łamania linii)
class Q{public static void main(String[]a){char q=34;String t="class Q{public static void main(String[]a){char
q=34;String t=;System.out.println(t.substring(0,62)+q+t+q+t.substring(62));}}";System.out.println(t.substring
(0,62)+q+t+q+t.substring(62));}}
unescape(q="unescape(q=%220%22).replace(0,q)").replace(0,q)
Inny pokazujący rzadko używaną w JavaScripcie funkcjonalność:
function f()
{
alert(f.toString()+"f()");
}f()
LISP
[edytuj | edytuj kod] (funcall (lambda (x)
(append x (list (list 'quote x))))
'(funcall (lambda (x)
(append x (list (list 'quote x))))))
Krótszy:
:X
a='a=%c%s%c;a=sprintf(a,39,a,39);disp(a);';a=sprintf(a,39,a,39);disp(a);
(fun s -> Printf.printf "%s %S" s s) "(fun s -> Printf.printf \"%s %S\" s s)"
const a='const a=';b='begin write(a,#39,a,#39#59#98#61#39,b,#39#59#10,b) end.';
begin write(a,#39,a,#39#59#98#61#39,b,#39#59#10,b) end.
Uwaga 1. W przypadku DOS-owej implementacji Pascala dane wyjściowe mogą wyglądać niekompletnie. Należy wtedy w kodzie zamienić "#10
" na "#13#10
" i dodać CR przed LF na końcu pierwszej linii.
Uwaga 2. Powyższy program może być napisany krócej zamieniając ") end.
" na ")end.
", ale nie wpływa to dobrze na czytelność. Dalsze skracanie może być osiągnięte poprzez usunięcie "#10
" i napisanie programu w jednej linii. Po tych zmianach program powinien wyglądać tak: (poprawność działania została potwierdzona używając Turbo Pascala dla DOS-a i Free Pascala dla Linuksa):
const a='const a=';b='begin write(a,#39,a,#39#59#98#61#39,b,#39#59,b)end.';begin write(a,#39,a,#39#59#98#61#39,b,#39#59,b)end.
Inny (Borland Pascal i Free Pascal):
const a='const a=;begin write(copy(a,1,8),#39,a,#39,copy(a,9,99)) end.';begin write(copy(a,1,8),#39,a,#39,copy(a,9,99)) end.
Inny (Borland Pascal i Free Pascal):
const a:string='const a:string=;begin insert(#39+a+#39,a,16);write(a) end.';begin insert(#39+a+#39,a,16);write(a) end.
Perl
[edytuj | edytuj kod] $_=q{$_=q{Q};s/Q/$_/;print};s/Q/$_/;print
Inny:
$_=q{print"\$_=q{$_};eval"};eval
Kolejny (shell+Perl):
perl -le '$n=q{perl -le a$n=q{$x};($_=$n)=~s/\141/\47/g;s/\$x/$n/;printa};($_=$n)=~s/\141/\47/g;s/\$x/$n/;print'
Ten quine oszukuje, używając DATA
aby uzyskać dostęp do własnego kodu:
seek DATA, 0, 0; print <DATA>
__DATA__
PHP
[edytuj | edytuj kod] <?
$a='chr(60).chr(63).chr(10).chr(36).chr(97).chr(61).chr(39).$a.chr(39).chr(59).chr(10)."echo $a;".chr(10).chr(63).chr(62)';
echo chr(60).chr(63).chr(10).chr(36).chr(97).chr(61).chr(39).$a.chr(39).chr(59).chr(10)."echo $a;".chr(10).chr(63).chr(62);
?>
<?
$a='<?
$a=2;
echo str_replace(1+1,chr(39).$a.chr(39),$a);
?>';
echo str_replace(1+1,chr(39).$a.chr(39),$a);
?>
Inny:
<?php $c='echo \'<?php $c=\\\'\'.addslashes($c).\'\\\';eval($c) ?>\';';eval($c) ?>
Inny: (trochę naciągany)
<? print file_get_contents(__FILE__); ?>
Uwaga: należy używać co najmniej PHP w wersji 4.3, aby uruchomić powyższy przykład.
PL1
[edytuj | edytuj kod](Uwaga: Ten najmniejszy możliwy quine w PL/I należy skompilować używając kompilatora OS PL/I V2.3.0, ale wymaga on lewego marginesu ustawionego na 1 i opcji COMPILE unieważniającej znaczącą ilość poważnych błędów i ostrzeżeń)
%dcl z%z='put edit';proc options(main;q=''''put list(m;do i=1,2;z(q)skip;do j=
1to 78c=substr(m(i),j;if c=q z(c;z(c;end;z(q',';dcl(c,q)char,m(2)char(99)init(
'%dcl z%z=''put edit'';proc options(main;q=''''''''put list(m;do i=1,2;z(q)skip;do j=',
'1to 78c=substr(m(i),j;if c=q z(c;z(c;end;z(q'','';dcl(c,q)char,m(2)char(99)init(',
(dup == {dup cvx exec} pop 8 12 getinterval =)
dup cvx exec
a='a=%s;print a%%`a`';print a%`a`
Inny:
b='\\';g='"';p='%';s="b='%s%s';g='%s';p='%s';s=%s%s%s;print s%s(b,b,g,p,g,s,g,p)";print s%(b,b,g,p,g,s,g,p)
Kolejny:
b,g,p,s='\\','"','%',"b,g,p,s='%s%s','%s','%s',%s%s%s;print s%s(b,b,g,p,g,s,g,p)";print s%(b,b,g,p,g,s,g,p)
Jeszcze inny, bardziej czytelny:
i = r'"i = r\'" + i + "\'\nprint " + i'
print "i = r\'" + i + "\'\nprint " + i
Ruby
[edytuj | edytuj kod] puts <<2*2,2
puts <<2*2,2
2
Inny, w pewnym stopniu mniej czytelny:
_="puts'_='+_.inspect+';'+_";puts'_='+_.inspect+';'+_
((lambda (x)
(list x (list (quote quote) x)))
(quote
(lambda (x)
(list x (list (quote quote) x)))))
Tcl
[edytuj | edytuj kod] puts [join [split "puts \[a{a}]" a] {join [split "puts \[a{a}]" a] }]
a="a="":b=left(a,3):c=mid(a,3):msgbox(b+b+c+c)":b=left(a,3):c=mid(a,3):msgbox(b+b+c+c)
CLEAR
SET TALK OFF
SET TEXTMERGE ON
\CLEAR
\SET TALK OFF
\SET TEXTMERGE ON
Kod maszynowy (plik COM dla MS-DOS)
[edytuj | edytuj kod]Znaki poniżej mają te same kody co znaki wyprowadzane przez quine, jednak tutaj wyglądają inaczej ze względu na inne formatowanie. Otworzenie kodu z jakimś prostym edytorze tekstowym (np. edit.com z MS-DOS) pokazuje znaki wyprowadzane przez program, co dowodzi, że to jest quine:
´@»�¹�ºÍ!´@Í!ô@»�¹�ºÍ!´@Í!Ã
Powyższy tekst jest wynikiem interpretacji instrukcji w szesnastkowych przez komputer jako znaków. Same instrukcje HEX są poniżej. Aby uzyskać program wykonywalny należy użyć edytora szesnastkowego.
b4 40 bb 01 00 b9 12 00 ba 12 01 cd 21 b4 40 cd 21 c3 b4 40 bb 01 00 b9 12 00 ba 12 01 cd 21 b4 40 cd 21 c3
Przykłady plików skompresowanych
[edytuj | edytuj kod]gzip
[edytuj | edytuj kod]Poniższy 204-bajtowy plik gzip rozpakowuje się do takiego samego pliku[1]:
0000000 1f 8b 08 08 00 00 00 00 00 ff 71 75 69 6e 65 2e
0000020 67 7a 00 00 18 00 e7 ff 1f 8b 08 08 00 00 00 00
0000040 00 ff 71 75 69 6e 65 2e 67 7a 00 00 18 00 e7 ff
0000060 42 16 47 16 07 00 05 00 fa ff 42 16 47 16 07 00
0000100 05 00 fa ff 00 05 00 fa ff 00 14 00 eb ff 42 16
0000120 47 16 07 00 05 00 fa ff 00 05 00 fa ff 00 14 00
0000140 eb ff 42 88 21 c4 00 00 14 00 eb ff 42 88 21 c4
0000160 00 00 14 00 eb ff 42 88 21 c4 00 00 14 00 eb ff
0000200 42 88 21 c4 00 00 14 00 eb ff 42 88 21 c4 00 00
0000220 00 00 ff ff 00 00 00 ff ff 00 0d 00 f2 ff 42 88
0000240 21 c4 00 00 00 00 ff ff 00 00 00 ff ff 00 0d 00
0000260 f2 ff 83 70 a0 1c 00 ff 79 ff a9 cc 00 00 00 83
0000300 70 a0 1c 00 ff 79 ff a9 cc 00 00 00
Przypisy
[edytuj | edytuj kod]- ↑ How to make compressed file quines, step by step | The Blorg [online], blog.matthewbarber.io [dostęp 2019-07-27] (ang.).
Linki zewnętrzne
[edytuj | edytuj kod]- Strona o quine (Gary’ego P. Thompsona)
- Dyskusja Davida Madore'a o quine
- Strona poświęcona W.V.O Quine'owi