From madore@news.ens.fr
Path: eleves!not-for-mail
From: madore@news.ens.fr (GroTeXdieck)
Newsgroups: ens.forum.informatique.lang
Subject: Programmes self-rep
Date: 3 Jun 1999 22:50:39 GMT
Lines: 50
Sender: madore@clipper.ens.fr
Message-ID: <7j70rv$o84$1@clipper.ens.fr>
NNTP-Posting-Host: clipper.ens.fr
Mime-Version: 1.0
Content-Type: text/plain; charset=ISO-8859-1
Content-Transfer-Encoding: 8bit
X-Trace: clipper.ens.fr 928450239 24836 129.199.129.1 (3 Jun 1999 22:50:39 GMT)
X-Complaints-To: forum@clipper.ens.fr
NNTP-Posting-Date: 3 Jun 1999 22:50:39 GMT
X-Newsreader: Flrn (0.4pre2 - 05/99)
Xref: eleves ens.forum.informatique.lang:129

Pour animer un peu ce conti croupissant, je propose de jouer un peu
avec les programmes self-rep dans différents langages de
programmation.

Rappelons qu'un programme self-rep c'est un programme qui affiche son
propre listing.  Pour faire simple, disons que le théorème du point
fixe assure que tout langage de programmation Turing-complet (et
possédant un dispositif de sortie) permet d'écrire un programme
self-rep.  Parfois c'est très facile (comme le 10 LIST du BASIC),
parfois carrément trivial (le programme vide), mais en tout cas c'est
toujours possible, et normalement il n'y a pas besoin de hack affreux
(enfin, le langage peut être nacsze et alors ça être laid, voire
effectivement hackesque s'il y a des limitations bizarres sur la
sortie par exemple).

L'idée théorique c'est que si la fonction « quine » affiche son
argument suivi de son argument quoté comme un appel de fonction (i.e.
« quine('double-bubble') » affiche « double-bubble('double-bubble') »
pour reprendre l'exemple de Hofstadter) alors « quine('quine') » va
faire ce qu'il faut.  Ce qu'il faut retenir en tout cas c'est qu'une
bonne partie du programme (le « quine » en l'occurrence) va se
retrouver deux fois, une fois comme corps du programme et une fois
comme donnée à utiliser pour réconstituer le listing.

Les programme self-rep sont même cofinaux dans les programmes.  Il en
existe d'arbitrairement longs.  On peut même, dans un certain sens,
rendre tout programme self-rep, en lui faisant faire sa tâche
habituelle, *puis* afficher son listing.  On peut imaginer des choses
plus baroques : un programme A dans un langage X qui écrit le listing
d'un programme B dans un langage Y qui écrit à son tour le listing de
A (ça c'est facile : souvenons-nous que B peut très bien se contenter
d'une suite de printf()) ; ou n programmes dans n langages différents
chacun desquels demande à l'utilisateur de choisir un des n, et
imprime son listing (ça ça devrait être rigolo).

Dans certains langages de programmation on dispose d'une fonction
d'évaluation.  Dans ce cas, on peut simplifier le programme self-rep
en réutilisant la même chaîne pour l'exécuter et pour l'afficher.
C'est amusant, mais c'est quand même de la gruge et ça ne rentre pas
dans le cadre de la théorie générale.  Cependant, comme j'ai composé
des programmes self-rep pour m'amuser, je n'ai pas manqué de faire
appel à cette feature.

Je vais poster quelques exemples que j'ai composé ce soir, dans
différents langages.  Je vous invite à essayer les langages que j'ai
omis (par exemple TeX, perl, caml, fortran).  Il faut quand même
essayer que le programme soit non-trivial (par exemple, « foobar » en
TeX ou m4 est selfrep, mais il ne montre pas la méthode générale) et
si possible assez élégant ou autrement digne d'intérêt.  Il faut
éviter les one-liners quand la ligne est ridiculement longue.

From madore@news.ens.fr
Path: eleves!not-for-mail
From: madore@news.ens.fr (GroTeXdieck)
Newsgroups: ens.forum.informatique.lang
Subject: Re: Programmes self-rep
Date: 3 Jun 1999 22:57:22 GMT
Lines: 155
Sender: madore@clipper.ens.fr
Message-ID: <7j718i$o84$3@clipper.ens.fr>
References: <7j70rv$o84$1@clipper.ens.fr>
NNTP-Posting-Host: clipper.ens.fr
Mime-Version: 1.0
Content-Type: text/plain; charset=ISO-8859-1
Content-Transfer-Encoding: 8bit
X-Trace: clipper.ens.fr 928450642 24836 129.199.129.1 (3 Jun 1999 22:57:22 GMT)
X-Complaints-To: forum@clipper.ens.fr
NNTP-Posting-Date: 3 Jun 1999 22:57:22 GMT
X-Newsreader: Flrn (0.4pre2 - 05/99)
Xref: eleves ens.forum.informatique.lang:130

Bon, commençons par le C.  On peut faire assez court et hackesque mais
on a alors une longue ligne, ce qui est mal :

-----
main(){char *s="main(){char *s=%c%s%c;printf(s,34,s,34,10);}%c";printf(s,34,s,34,10);}
-----

Ça peut faire un .sig éventuellement (d'ailleurs il me semble avoir vu
un truc du genre en .sig).

Bon, mais il est aussi possible de faire un bon programme C
correctement écrit et commenté, conforme à la norme ANSI, portable (pas
d'hypothèse sur le jeu de caractères utilisé) et proprement formaté. 
C'est un peu long, c'est tout :

-----
#include <stdio.h>

/* This program is a self-replicating program.  In other words, it
 * will print its own listing.  There are many such programs.  This
 * one, however, was designed in order to be elegant, portable, and
 * devoid of dirty hacks. */

/* The following characters must be quoted in a string. */
const char quote = '"';
const char escape = '\\';

/* Strings respectively prepended and appended before a quoted
 * string. */
const char *qline1 = "  \"";
const char *qline2 = "\",\n";

void
printquoted (const char *s)
     /* Quote print a character string. */
{
  int i;

  printf ("%s", qline1);
  for ( i=0 ; s[i] ; i++ )
    {
      if ( ( s[i] == quote ) || ( s[i] == escape ) )
	printf ("%c", escape);
      printf ("%c", s[i]);
    }
  printf ("%s", qline2);
}

void
quine (const char *tab[], const char *middle)
     /* The Quine operation: this function prints the first part of
      * the tab board (up to NULL), then prints it again quoted, the
      * prints the middle part, then the second part quoted, and
      * finally the second part unquoted. */
{
  int i,j;

  for ( i=0 ; tab[i] ; i++ )
    printf ("%s\n", tab[i]);
  for ( j=0 ; tab[j] ; j++ )
    printquoted (tab[j]);
  printf ("%s\n", middle);
  for ( j++ ; tab[j] ; j++ )
    printquoted (tab[j]);
  for ( i++ ; tab[i] ; i++)
    printf ("%s\n", tab[i]);
}

/* The string to be printed in the "middle", between the first and
 * second parts. */
const char *middle = "  NULL, /* And now the \"second\" part */";

const char *tab[] = {
  "#include <stdio.h>",
  "",
  "/* This program is a self-replicating program.  In other words, it",
  " * will print its own listing.  There are many such programs.  This",
  " * one, however, was designed in order to be elegant, portable, and",
  " * devoid of dirty hacks. */",
  "",
  "/* The following characters must be quoted in a string. */",
  "const char quote = '\"';",
  "const char escape = '\\\\';",
  "",
  "/* Strings respectively prepended and appended before a quoted",
  " * string. */",
  "const char *qline1 = \"  \\\"\";",
  "const char *qline2 = \"\\\",\\n\";",
  "",
  "void",
  "printquoted (const char *s)",
  "     /* Quote print a character string. */",
  "{",
  "  int i;",
  "",
  "  printf (\"%s\", qline1);",
  "  for ( i=0 ; s[i] ; i++ )",
  "    {",
  "      if ( ( s[i] == quote ) || ( s[i] == escape ) )",
  "	printf (\"%c\", escape);",
  "      printf (\"%c\", s[i]);",
  "    }",
  "  printf (\"%s\", qline2);",
  "}",
  "",
  "void",
  "quine (const char *tab[], const char *middle)",
  "     /* The Quine operation: this function prints the first part of",
  "      * the tab board (up to NULL), then prints it again quoted, the",
  "      * prints the middle part, then the second part quoted, and",
  "      * finally the second part unquoted. */",
  "{",
  "  int i,j;",
  "",
  "  for ( i=0 ; tab[i] ; i++ )",
  "    printf (\"%s\\n\", tab[i]);",
  "  for ( j=0 ; tab[j] ; j++ )",
  "    printquoted (tab[j]);",
  "  printf (\"%s\\n\", middle);",
  "  for ( j++ ; tab[j] ; j++ )",
  "    printquoted (tab[j]);",
  "  for ( i++ ; tab[i] ; i++)",
  "    printf (\"%s\\n\", tab[i]);",
  "}",
  "",
  "/* The string to be printed in the \"middle\", between the first and",
  " * second parts. */",
  "const char *middle = \"  NULL, /* And now the \\\"second\\\" part */\";",
  "",
  "const char *tab[] = {",
  NULL, /* And now the "second" part */
  "  NULL",
  "};",
  "",
  "int",
  "main (void)",
  "{",
  "  quine (tab, middle);",
  "  return 0;",
  "}",
  NULL
};

int
main (void)
{
  quine (tab, middle);
  return 0;
}
-----

Pas grand chose à dire, sinon que le principe « quine » a été coupé en
deux, et ça donne quelque chose comme « qui('qui','ne')ne », et du
coup il faut introduire un middle string (« ',' » dans cette analogie,
voir la variable « middle » pour la valeur réelle).

From madore@news.ens.fr
Path: eleves!not-for-mail
From: madore@news.ens.fr (GroTeXdieck)
Newsgroups: ens.forum.informatique.lang
Subject: Re: Programmes self-rep
Date: 3 Jun 1999 23:00:08 GMT
Lines: 127
Sender: madore@clipper.ens.fr
Message-ID: <7j71do$ps7$1@clipper.ens.fr>
References: <7j70rv$o84$1@clipper.ens.fr>
NNTP-Posting-Host: clipper.ens.fr
Mime-Version: 1.0
Content-Type: text/plain; charset=ISO-8859-1
Content-Transfer-Encoding: 8bit
X-Trace: clipper.ens.fr 928450809 26503 129.199.129.1 (3 Jun 1999 23:00:08 GMT)
X-Complaints-To: forum@clipper.ens.fr
NNTP-Posting-Date: 3 Jun 1999 23:00:08 GMT
X-Newsreader: Flrn (0.4pre2 - 05/99)
Xref: eleves ens.forum.informatique.lang:131

Le programme Java que j'ai pondu ressemble beaucoup à mon programme C. 
Il n'est pas commenté et il est un peu pourri (je ne connais presque
pas le Java) mais sinon il est très semblable.

-----
public class SelfRep {
    static final char quote = '"';
    static final char escape = '\\';
    public static String quote (String s) {
	int i;
	StringBuffer buf = new StringBuffer ();
	for ( i=0 ; i<s.length() ; i++ ) {
	    if ( ( s.charAt (i) == quote )
		 || ( s.charAt (i) == escape ) )
		buf.append (escape);
	    buf.append (s.charAt (i));
	}
	return buf.toString ();
    }
    static public class QuineData {
	static final String qline1 = "	    \"";
	static final String qline2 = "\",";
	String predata[];
	String meddata[];
	String postdata[];
	public QuineData (String predata[], String meddata[],
			  String postdata[]) {
	    this.predata = predata;
	    this.meddata = meddata;
	    this.postdata = postdata;
	}
	public void quine () {
	    int i;
	    for ( i=0 ; i<predata.length ; i++ )
		System.out.println (predata[i]);
	    for ( i=0 ; i<predata.length ; i++ ) {
		System.out.print (qline1);
		System.out.print (quote (predata[i]));
		System.out.println (qline2);
	    }
	    for ( i=0 ; i<meddata.length ; i++ )
		System.out.println (meddata[i]);
	    for ( i=0 ; i<postdata.length ; i++ ) {
		System.out.print (qline1);
		System.out.print (quote (postdata[i]));
		System.out.println (qline2);
	    }
	    for ( i=0 ; i<postdata.length ; i++ )
		System.out.println (postdata[i]);
	}
    }
    public static void main (String[] args) {
	String predata[] = {
	    "public class SelfRep {",
	    "    static final char quote = '\"';",
	    "    static final char escape = '\\\\';",
	    "    public static String quote (String s) {",
	    "	int i;",
	    "	StringBuffer buf = new StringBuffer ();",
	    "	for ( i=0 ; i<s.length() ; i++ ) {",
	    "	    if ( ( s.charAt (i) == quote )",
	    "		 || ( s.charAt (i) == escape ) )",
	    "		buf.append (escape);",
	    "	    buf.append (s.charAt (i));",
	    "	}",
	    "	return buf.toString ();",
	    "    }",
	    "    static public class QuineData {",
	    "	static final String qline1 = \"	    \\\"\";",
	    "	static final String qline2 = \"\\\",\";",
	    "	String predata[];",
	    "	String meddata[];",
	    "	String postdata[];",
	    "	public QuineData (String predata[], String meddata[],",
	    "			  String postdata[]) {",
	    "	    this.predata = predata;",
	    "	    this.meddata = meddata;",
	    "	    this.postdata = postdata;",
	    "	}",
	    "	public void quine () {",
	    "	    int i;",
	    "	    for ( i=0 ; i<predata.length ; i++ )",
	    "		System.out.println (predata[i]);",
	    "	    for ( i=0 ; i<predata.length ; i++ ) {",
	    "		System.out.print (qline1);",
	    "		System.out.print (quote (predata[i]));",
	    "		System.out.println (qline2);",
	    "	    }",
	    "	    for ( i=0 ; i<meddata.length ; i++ )",
	    "		System.out.println (meddata[i]);",
	    "	    for ( i=0 ; i<postdata.length ; i++ ) {",
	    "		System.out.print (qline1);",
	    "		System.out.print (quote (postdata[i]));",
	    "		System.out.println (qline2);",
	    "	    }",
	    "	    for ( i=0 ; i<postdata.length ; i++ )",
	    "		System.out.println (postdata[i]);",
	    "	}",
	    "    }",
	    "    public static void main (String[] args) {",
	    "	String predata[] = {",
	};
	String postdata[] = {
	    "	};",
	    "	String meddata[] = {",
	    "	    \"	};\",",
	    "	    \"	String postdata[] = {\",",
	    "	};",
	    "	QuineData prog = new QuineData (predata, meddata, postdata);",
	    "	prog.quine();",
	    "    }",
	    "}",
	};
	String meddata[] = {
	    "	};",
	    "	String postdata[] = {",
	};
	QuineData prog = new QuineData (predata, meddata, postdata);
	prog.quine();
    }
}
-----

Ce qui est amusant (et je vous invite à méditer là-dessus) c'est que
bien qu'on en aurait envie, on ne peut pas définir meddata entre
predata et postdata, il faut le mettre soit avant predata soit après
postdata).

From madore@news.ens.fr
Path: eleves!not-for-mail
From: madore@news.ens.fr (GroTeXdieck)
Newsgroups: ens.forum.informatique.lang
Subject: Re: Programmes self-rep
Date: 3 Jun 1999 23:10:06 GMT
Lines: 79
Sender: madore@clipper.ens.fr
Message-ID: <7j720e$ps7$2@clipper.ens.fr>
References: <7j70rv$o84$1@clipper.ens.fr>
NNTP-Posting-Host: clipper.ens.fr
Mime-Version: 1.0
Content-Type: text/plain; charset=ISO-8859-1
Content-Transfer-Encoding: 8bit
X-Trace: clipper.ens.fr 928451406 26503 129.199.129.1 (3 Jun 1999 23:10:06 GMT)
X-Complaints-To: forum@clipper.ens.fr
NNTP-Posting-Date: 3 Jun 1999 23:10:06 GMT
X-Newsreader: Flrn (0.4pre2 - 05/99)
Xref: eleves ens.forum.informatique.lang:132

En shell, on peut gruger carrément :

-----
#! /bin/sh
cat $0
-----

(on pouvait gruger comme ça en C, sauf que argv[0] renvoie le truc
compilé, pas le source ;-).

Pour faire un self-rep non trivial, il va falloir procéder comme en C. 
Au lieu de prendre un tableau, j'utilise un fichier temporaire (et j'en
prends deux, un pour le predata et un pour le postdata ; ici il n'y a
pas de meddata).  La commande sed permet de faire facilement les
quotages (à vrai dire je m'en suis servi pour produire la partie
médiane du programme parce que compter les backslashes ça devenait
lourd).

-----
#! /bin/sh
PREFILE=`mktemp /tmp/selfrep.XXXXXX` || exit 1
POSTFILE=`mktemp /tmp/selfrep.XXXXXX` || exit 1
echo "#! /bin/sh" >>$PREFILE
echo "PREFILE=\`mktemp /tmp/selfrep.XXXXXX\` || exit 1" >>$PREFILE
echo "POSTFILE=\`mktemp /tmp/selfrep.XXXXXX\` || exit 1" >>$PREFILE
echo "cat \$PREFILE" >>$POSTFILE
echo "cat \$PREFILE | sed -e 's/\\([\`\"\$\\]\\)/\\\\\\1/g' | \\" >>$POSTFILE
echo "    sed -e 's/^/echo \"/' | sed -e 's/\$/\" >>\$PREFILE/'" >>$POSTFILE
echo "cat \$POSTFILE | sed -e 's/\\([\`\"\$\\]\\)/\\\\\\1/g' | \\" >>$POSTFILE
echo "    sed -e 's/^/echo \"/' | sed -e 's/\$/\" >>\$POSTFILE/'" >>$POSTFILE
echo "cat \$POSTFILE" >>$POSTFILE
echo "rm \$PREFILE" >>$POSTFILE
echo "rm \$POSTFILE" >>$POSTFILE
cat $PREFILE
cat $PREFILE | sed -e 's/\([`"$\]\)/\\\1/g' | \
    sed -e 's/^/echo "/' | sed -e 's/$/" >>$PREFILE/'
cat $POSTFILE | sed -e 's/\([`"$\]\)/\\\1/g' | \
    sed -e 's/^/echo "/' | sed -e 's/$/" >>$POSTFILE/'
cat $POSTFILE
rm $PREFILE
rm $POSTFILE
-----

Par ailleurs, le shell possède une fonction eval, donc on peut s'en
servir.  L'ennui, c'est qu'apparemment /bin/sh ne tolère pas les
retours chariot dans les noms de variable.  Qu'à cela ne tienne.  On va
utiliser l'espace à la place (et se servir de for i in) ; comme on veut
quand même pouvoir mettre des espaces dans les commandes, on va mettre
des traits de soulignements à leur place, et utiliser \_ pour
représenter _.  Le nombre de backslashes devient carrément
impressionnant par endroits...

-----
#! /bin/sh
x="
echo_\"#!_/bin/sh\"
echo_\"x=\\\"\"
for_u_in_\$x;_do_echo_\$u_|_sed_-e_'s/\\([\`\"\$\\]\\)/\\\\\\1/g';_done
echo_\"\\\"\"
echo_\"for_i_in_\\\$x;_do\"
echo_\"eval_\\\`echo_\\\$i_|_sed_-e_'s/\\\\([^\\\\]\\\\)\\_/\\\\1_/g'_|_\\\\\"
echo_\"sed_-e_'s/\\\\\\\\\\_/\\_/g'\\\`\"
echo_\"done\"
"
for i in $x; do
eval `echo $i | sed -e 's/\([^\]\)_/\1 /g' | \
sed -e 's/\\_/_/g'`
done
-----

Il faut imaginer que x est écrit dans un langage bizarre (qui ressemble
beaucoup au shell mais avec des _ en lieu d'espaces).  Mais dans ce
langage on peut accéder au programme en cours (sous le nom de $x) donc
on va réitérer la gruge initiale avec le $0.  Il ne reste plus qu'à
mettre ce programme dans une variable shell (x), et ne pas oublier
d'écrire (dans les quatre dernières lignes) un interpréteur du langage
bizarre, que le programme x doit aussi afficher (avec des echo). 
Exercice : expliquer le sens de chacun des backslashes là où il y en a
10 d'affilée.

From madore@news.ens.fr
Path: eleves!not-for-mail
From: madore@news.ens.fr (GroTeXdieck)
Newsgroups: ens.forum.informatique.lang
Subject: Re: Programmes self-rep
Date: 3 Jun 1999 23:19:15 GMT
Lines: 42
Sender: madore@clipper.ens.fr
Message-ID: <7j72hj$ps7$3@clipper.ens.fr>
References: <7j70rv$o84$1@clipper.ens.fr>
NNTP-Posting-Host: clipper.ens.fr
Mime-Version: 1.0
Content-Type: text/plain; charset=ISO-8859-1
Content-Transfer-Encoding: 8bit
X-Trace: clipper.ens.fr 928451955 26503 129.199.129.1 (3 Jun 1999 23:19:15 GMT)
X-Complaints-To: forum@clipper.ens.fr
NNTP-Posting-Date: 3 Jun 1999 23:19:15 GMT
X-Newsreader: Flrn (0.4pre2 - 05/99)
Xref: eleves ens.forum.informatique.lang:133

En dc, un programme selfrep s'écrit très facilement :

-----
[91PdP93PP10P]91PdP93PP10P
-----

Il faut lire ça comme ceci : on prend la chaîne entre crochets, on la
met sur la pile.  Puis on prend 91 et on l'affiche (P), c'est-à-dire
qu'on afficher un crochet ouvert ([ a le code ASCII 91).  Puis on
duplique (d) la chaîne qu'on avait mis sur la pile et on en affiche
(P) une.  Puis on affiche un crochet fermé (93P) puis la chaîne à
nouveau (P - qui va chercher la copie qu'on avait mis sur la pile).
Et enfin on affiche un retour chariot.  Là, on a le
« quine('quine') » dans toute sa splendide beauté, avec quine =
91PdP93PP10P, si ce n'est que c'est un langage à pile donc c'est
[quine]quine.

Comme dc a aussi une fonction d'évaluation (x), on peut l'utiliser
pour faire légèrement plus concis :

-----
[91PP93P[dx]P10P]dx
-----

Autrement dit, on prend toute la chaîne (entre les crochets
extérieurs), on la duplique (d) et on en exécute une copie (x).  La
chaîne en question, elle affiche un crochet ouvert (91P), puis elle
s'affiche tout entière (P), suivie d'un crochet fermé (93P), de la
chaîne « dx » ([dx]P) et enfin d'un retour chariot (10P).  La
technique est donc la même que dans l'exemple en shell mais je ne sais
pas ce qui est le plus illisible.

Sans autre but que de faire plus long et de passer par une variable,
il y a aussi l'exemple suivant :

-----
[91PlxP93P[sxlxx]P10P]sxlxx
-----

qui utilise une variable x pour stocker la chaîne plutôt que de la
mettre sur la pile (ça ressemble donc encore plus à mon exemple en
shell).

From madore@news.ens.fr
Path: eleves!not-for-mail
From: madore@news.ens.fr (GroTeXdieck)
Newsgroups: ens.forum.informatique.lang
Subject: Re: Programmes self-rep
Date: 3 Jun 1999 23:25:55 GMT
Lines: 21
Sender: madore@clipper.ens.fr
Message-ID: <7j72u3$ps7$4@clipper.ens.fr>
References: <7j70rv$o84$1@clipper.ens.fr>
NNTP-Posting-Host: clipper.ens.fr
Mime-Version: 1.0
Content-Type: text/plain; charset=ISO-8859-1
Content-Transfer-Encoding: 8bit
X-Trace: clipper.ens.fr 928452355 26503 129.199.129.1 (3 Jun 1999 23:25:55 GMT)
X-Complaints-To: forum@clipper.ens.fr
NNTP-Posting-Date: 3 Jun 1999 23:25:55 GMT
X-Newsreader: Flrn (0.4pre2 - 05/99)
Xref: eleves ens.forum.informatique.lang:134

Comme j'en suis l'inventeur, il faut bien que je parle du SIMPLE.  Mon
programme self-rep en SIMPLE il donne ceci :

-----
<def|quine|[@,1@<inputform|@,1@>`|%
<inputform|@,2@>@,2@]>%
<quine|`<def`|quine`|`[`@,1`@`<inputform`|`@,1`@`>```|`%
`<inputform`|`@,2`@`>`@,2`@`]`>`%
`<quine`||`>`%
>%
-----

En SIMPLE on a le méchanisme de quotation tout prêt (c'est la fonction
inputform).  On définit la fonction quine, qui va renvoyer son premier
argument (@,1@ ; la virgule signifie sans évaluation, mais il y a
toujours une évaluation de plus qu'on oublie) suivi de son premier
argument quoté, puis le caractère « | », puis son second argument
quoté, et enfin son second argument non quoté.  Autrement dit, les
deux arguments de quine sont le predata et le postdata de mes exemples
précédents.  Ensuite, j'appelle quine avec les deux arguments Qui Vont
Bien (quotés comme le fera la fonction inputform).

From madore@news.ens.fr
Path: eleves!not-for-mail
From: madore@news.ens.fr (GroTeXdieck)
Newsgroups: ens.forum.informatique.lang
Subject: Re: Programmes self-rep
Date: 3 Jun 1999 23:49:40 GMT
Lines: 89
Sender: madore@clipper.ens.fr
Message-ID: <7j74ak$ps7$5@clipper.ens.fr>
References: <7j70rv$o84$1@clipper.ens.fr>
NNTP-Posting-Host: clipper.ens.fr
Mime-Version: 1.0
Content-Type: text/plain; charset=ISO-8859-1
Content-Transfer-Encoding: 8bit
X-Trace: clipper.ens.fr 928453780 26503 129.199.129.1 (3 Jun 1999 23:49:40 GMT)
X-Complaints-To: forum@clipper.ens.fr
NNTP-Posting-Date: 3 Jun 1999 23:49:40 GMT
X-Newsreader: Flrn (0.4pre2 - 05/99)
Xref: eleves ens.forum.informatique.lang:135

Passons au langage béni par les dieux : le Scheme.

Le Scheme a l'avantage (un peu comme le SIMPLE) d'avoir une fonction
write qui imprime une sexp sous forme d'entrée.  Ça va nous permettre
d'éviter les gymnastiques avec les chaînes de caractères qu'on a fait
dans tous les langages jusqu'à maintenant.

Si on avait défini quine comme il faut, (quine 'quine) afficherait
(quine 'quine).  Comment faut-il le définir comme il faut ?  Eh bien on
veut que (quote x) appelle write avec x et 'x comme arguments.  Là, il
s'agit de quoter la valeur de x et cela se fait avec quasiquote,
précisément (write `(,x ',x)), ce qui signifie appeler write avec [` =
sans évaluation] ([, = ben si, on évalue : valeur de]x, [' = vraiment
pas d'évaluation][, = valeur de] x).  Bref, si on fait (define (quine
x) (write `(,x ',x))) alors (quine 'quine) va afficher (quine (quote
quine)), ce qui est presque bon à part que le ' a été remplacé par sa
désabréviation qui est quote.  On peut donc faire (define (quine x)
(write (quasiquote ((unquote x) (quote (unquote x)))))) puis (quine
(quote quine)).  Bon, sauf que dans tout ça la définition de quine
n'est pas affichée.  Si on veut l'afficher, on la supprime, i.e. on
remplace quine par sa définition dans l'expression (quine (quote
quine)) et on tombe sur l'horrible one-liner suivant :

-----
((lambda (x) (write (quasiquote ((unquote x) (quote (unquote x)))))) (quote (lambda (x) (write (quasiquote ((unquote x) (quote (unquote x))))))))
-----

Si vous êtes perdu dans les niveaux de quote, on peut aussi définir
quine de manière plus compréhensible par (define (quine x) (write (list
x (list 'quote x)))) ce qui est exactement la même chose que le truc
précédent.  Le one-liner devient alors

-----
((lambda (x) (write (list x (list (quote quote) x)))) (quote (lambda (x) (write (list x (list (quote quote) x))))))
-----

(lequel est le plus atroce à votre avis ?).

Bon, mais ça c'était pour la théorie.  Ces trucs n'affichent même pas
de retour chariot à la fin donc c'est MAL.  Si on veut un vrai selfrep,
on fait comme tout le monde, on définit une fonction (do) qui prend une
liste de sexps et qui les affiche, affiche un truc intermédiaire,
réaffiche les mêmes sexps, puis affiche un truc final.  Grâce à la
manière dont les quotations se font en scheme, on n'a pas besoin de
mécanisme complexe de quotation (juste un ' devant la liste) ; à part
ça il faut faire attention à mettre précisément une sexp par ligne.  Ça
donne le petit bijou suivant :

-----
(define (line-write x) (write x) (newline))
(define (d l) (map line-write l))
(define (mid) (display "(do '(") (newline))
(define (end) (display "))") (newline))
(define (do l) (d l) (mid) (d l) (end))
(do '(
(define (line-write x) (write x) (newline))
(define (d l) (map line-write l))
(define (mid) (display "(do '(") (newline))
(define (end) (display "))") (newline))
(define (do l) (d l) (mid) (d l) (end))
))
-----

(Mais là j'ai été obligé de recourir à display alors que les deux
one-liners qui précédaient avaient la beauté de faire seulement du
write.)

Le scheme (certains schemes du moins) a une fonction eval.  On peut
donc s'en servir comme on a fait dans les précédents langages à eval
(le shell, dc).  C'est quand même plus beau en scheme :

-----
(define x '(
(display "(define x '(")
(newline)
(map (lambda (s) (write s) (newline)) x)
(display "))")
(newline)
(display "(map eval x)")
(newline)
))
(map eval x)
-----

La fonction map applique son deuxième argument à tous les éléments de
son troisième argument (une liste).  On définit donc un petit programme
x, succession de sexps.  Pour l'exécuter, on fait (map eval x).  Pour
l'afficher, à raison d'une sexp par ligne, on mappe (lambda (s) (write
s) (newline)) à x.  Le reste est de routine.

From madore@news.ens.fr
Path: eleves!not-for-mail
From: madore@news.ens.fr (GroTeXdieck)
Newsgroups: ens.forum.informatique.lang
Subject: Re: Programmes self-rep
Date: 3 Jun 1999 23:55:38 GMT
Lines: 75
Sender: madore@clipper.ens.fr
Message-ID: <7j74lq$ps7$6@clipper.ens.fr>
References: <7j70rv$o84$1@clipper.ens.fr>
NNTP-Posting-Host: clipper.ens.fr
Mime-Version: 1.0
Content-Type: text/plain; charset=ISO-8859-1
Content-Transfer-Encoding: 8bit
X-Trace: clipper.ens.fr 928454138 26503 129.199.129.1 (3 Jun 1999 23:55:38 GMT)
X-Complaints-To: forum@clipper.ens.fr
NNTP-Posting-Date: 3 Jun 1999 23:55:38 GMT
X-Newsreader: Flrn (0.4pre2 - 05/99)
Xref: eleves ens.forum.informatique.lang:136

Je termine mes exemples avec le PostScript.  Là, il s'agit de dessiner
son propre listing.  Ça n'a rien de vraiment compliqué.  Je suis
l'exemple de mon programme Java.  Comme manipuler des chaîne en
PostScript est quand même lourd, j'utilise le fait que des parenthèses
correctement emboîtées n'ont pas besoin d'être quotées dans une chaîne
(les chaînes de caractères sont délimitées par des parenthèses en
PostScript).  J'évite donc complètement l'usage du backslash.  Comme
j'ai besoin d'une parenthèse ouvrante seule et d'une parenthèse
fermante seule, je les obtiens comme sous-chaînes de la chaîne « () ». 
C'est un peu de la gruge mais ça simplifie quand même beaucoup les
choses.  Bref, on a :

-----
%!
/size 10 def /y 825 def /x0 15 def
/Courier size selectfont
/newln {
  /y y size sub def
  x0 y moveto
} bind def
newln
/lead-and-trail (  ()) def
/lead lead-and-trail 0 3 getinterval def
/trail lead-and-trail 3 1 getinterval def
/quine {
  /med exch def /post exch def /pre exch def
  pre { show newln } forall pre { lead show show trail show newln } forall
  med { show newln } forall
  post { lead show show trail show newln } forall post { show newln } forall
} bind def
[
  (%!)
  (/size 10 def /y 825 def /x0 15 def)
  (/Courier size selectfont)
  (/newln {)
  (  /y y size sub def)
  (  x0 y moveto)
  (} bind def)
  (newln)
  (/lead-and-trail (  ()) def)
  (/lead lead-and-trail 0 3 getinterval def)
  (/trail lead-and-trail 3 1 getinterval def)
  (/quine {)
  (  /med exch def /post exch def /pre exch def)
  (  pre { show newln } forall pre { lead show show trail show newln } forall)
  (  med { show newln } forall)
  (  post { lead show show trail show newln } forall post { show newln } forall)
  (} bind def)
  ([)
]
[
  (])
  ([)
  (  (]))
  (  ([))
  (])
  (quine)
  (showpage)
]
[
  (])
  ([)
]
quine
showpage
-----

Pour le reste c'est tout comme mon programme Java.  Les tableaux sont
délimités par des crochets.  Je passe le prédata, le postdata et le
meddata dans cet ordre sur la pile et j'appelle quine qui affiche le
prédata, puis le prédata avec des parenthèses autour, puis le meddata,
puis le postdata avec parenthèses, et enfin le postdata tout seul.

Bien sûr, si on omet le %! au début, le programme affiche encore son
propre listing, et pour cause...  ;-)

