lunedì 23 novembre 2009

Quine

No, non parlerò di quel Quine, non direttamente almeno!

Nello slang dei programmatori si dice quine un programma che produce il suo stesso listato (*) come output. E naturalmente sì, si chiama quine in onore di quel Willard van Orman Quine. Quindi, in fondo si ritorna sempre lì!

Ecco ad esempio un semplice quine in C:
char*f="char*f=%c%s%c;main() {printf(f,34,f,34,10);}%c"; main(){printf(f,34,f,34,10);}
Per vedere come funziona, su Linux copiate la linea su un file quine.c, usando un normale editor. Poi, compilate il file con
   gcc quine.c
Quindi, eseguite il file eseguibile ottenuto (a.out) su un terminale con
./a.out
Se siete su Windows, per vedere cosa viene fuori dovete avere un compilatore C/C++. Se non l'avete, scaricatelo da qui, e non fatevi spaventare dalla dicitura "beta". Dopo aver compilato, lanciate il file exe sempre da terminale.

In realtà, il quine di cui sopra è piuttosto banale, anche se non banalissimo da "inventare". Su questo sito troverete una intera collezione di quines nei principali linguaggi di programmazione.

Collegati ai quine ci sono i programmi C offuscati (**).


Il programma in figura (che potete trovare qui), stampa un frattale, ed ha la forma di un frattale!

Esiste ancora "The International Obfuscated C Code Contest (IOCCC)", una vera a propria gara a chi scrive il programma C più incredibile (nel senso che riesce a dare l'output più inaspettato). Dagli archivi del sito, tiro fuori questa fantastica realizzazione:

#define P(X)j=write(1,X,1)
#define C 39
int M[5000]={2},*u=M,N[5000],R=22,a[4],l[]={0,-1,C-1,-1},m[]={1,-C,-1,C},*b=N,
*d=N,c,e,f,g,i,j,k,s;main(){for(M[i=C*R-1]=24;f|d>=b;){c=M[g=i];i=e;for(s=f=0;
s<4;s++)if((k=m[s]+g)>=0&&k=16!=M[k]>=16))a[f++
]=s;if(f){f=M[e=m[s=a[rand()/(1+2147483647/f)]]+g];j=j < f?f:j;f+=c&-16*!j;M[g]=
c|1<< s;m[*d++=e]=f|1<<(s+2)%4;}else e="d">b++?b[-1]:e;}P(" ");for(s=C;--s;P("_")
)P(" ");for(;P("\n"),R--;P("|"))for(e=C;e--;P("_ "+(*u++/8)%2))P("| "+(*u/4)%2
);}


(clicca qui per scaricare il file shapiro.c)

Sembra una accozzaglia casuale di caratteri, non è vero? Invece, non riuscirete mai a indovinare l'output di questo programma, a meno che non proviate a compilarlo ed ad eseguirlo!

L'ultimo programmino che vi propongo oggi:
typedef struct n{int a:3,
b:29;struct n*c;}t;t*
f();r(){}m(u)t*u;{t*w,*z;
z=u->c,q(z),u->b=z->b*10,
w=u->c=f(),w->a=1,w->c=z->
c;}t*k;g(u)t*u;{t*z,*v,*p,
*x;z=u->c,q(z),u->b=z->b,v
=z->c,z->a=2,x=z->c=f(),x
->a=3,x->b=2,p=x->c=f(),p
->c=f(),p->c->a=1,p->c->c=
v;}int i;h(u)t*u;{t*z,*v,*
w;int c,e;z=u->c,v=z->c,q(
v),c=u->b,e=v->b,u->b=z->b
,z->a=3,z->b=c+1,e+9>=c&&(
q(z),e=z->b,u->b+=e/c,w=f(
),w->b=e%c,w->c=z->c,u->c=
w);}int(*y[4])()={r,m,g,h};
char *sbrk();main(){t*e,*p,*o;
o=f(),o->c=o,o->b=1,e=f(),
e->a=2,p=e->c=f(),p->b=2,
p->c=o,q(e),e=e->c,(void)write
(1,"2.",2);for(;;e=e->c){q(e),
e->b=write(1,&e->b["0123456789"],
1);}}t*f(){return i||(i=1000,
k=(t*)sbrk(i*sizeof(t))),k+--i;
}q(p)t*p;{(*y[p->a])(p);}

(clicca qui per scaricare il file august.c)

Se non lo fermate con control-C, prima o poi questo vi andrà in core dump...
ma vi darà in dono l'espansione decimale del numero di nepero!

Ce ne sono molti altri da riscoprire. Alcuni di essi purtroppo non compilano più: i compilatori moderni non permettono certe "porcate" come quelli di una volta! Io però mi diverto ancora come una volta a rivedere certe "oddities"!

(*) Mamma mia da quanto tempo non usavo la parola listato. Saranno 20 anni, per lo meno. Mi sento proprio un dinosauro!

(**) Quando ero più giovane (sigh), sul mio HD c'era un bella collezione di questi programmini strani. Poi, il tempo passa e io non ho più ritrovato quella preziosa directory. Stasera ero in vena di nostalgie, e il magico google ha fatto il resto.

5 commenti:

  1. Sì anch'io!
    La parola "listato" è scomparsa perché non si usa più fare il listato. Una volta (io ho cominciato con le schede perforate) era normale esaminare il codice riga per riga per stanare errori di battitura, oggi con gli IDE uno comincia a scrivere e ogni tanto prova a compilare giusto per fermarsi un attimo. E poi com'è cambiato il debug! Ma anche i linguaggi di programmazione sono cambiati, vuoi mettere i bei tempi del FORTRAN IV!
    Ancora: il C permette di scrivere come vuoi ma se un mio collaboratore scrive come nei tuoi esempi cessa di essere un mio collaboratore. Ah la suddivisione in colonne del FORTRAN: 1, 6, 7, 72, sigh.
    Penso però che dovresti postarlo su Algoland.

    RispondiElimina
  2. L'HTML ha scombinato un po' di cose: se provo a fare un copiaincolla del codice (sì, io lo chiamo "codice", non "listato") che scrivi dopo il codice frattale, mi dice che non matchano le parentesi...

    RispondiElimina
  3. @hronir: più che l'HTML, è l'editor di blogger che fa un po' di casino.
    Ho messo il link ai file .c, prova adesso a scaricarli direttamente.

    @juhan: ovviamente, non ci penso neanche a scrivere codice di tal fatta! Per non parlare di quello che farei ai miei studenti se si presentassero con codice così! Però, ogni tanto è carino vedere certi matti che si divertono in questo modo. A proposito, sul sito dei quine ci sono degli esempi di quine in fortran!

    Eccone uno:

    REAL*8F(6)/48H(7X'REAL*8F(6)/48H'6A8,1H//7X'PRINTF,F'/7X'END')
    PRINTF,F
    END

    (non c'è limite alla follia umana)

    RispondiElimina
  4. Sì ho visto il sito. Però per il Fortran usano il 77, troppo nuovo. In illo tempore (prima del 1980) si usava il IV, 1966. OK, c'erano delle release più aggiornate ma non standard. L'esempio che riporti ha la funzione PRINTF che non è standard, quella buona è PRINT.
    Naturalmente prima del 77 si scriveva tutto in maiuscolo. Ultimamente il fortran è degenerato, per fortuna l'ho abbandonato. Adesso mi trovo a fare quasi tutto con Python anche se ... OK sono OT.

    RispondiElimina

I commenti vengono gestiti tramite Disqus. Siete pregati di autentificarvi prima di commentare.