Programmation Fonctionnelle et Symbolique - Expressions Symboliques
- 2.1. Les Atomes
- 2.2. Les doublets
- 2.3. Notation des Listes
- 2.4. Représentation d'arbres par des listes
- 2.5. Représentation des Programmes
Résumé
Les expressions symboliques sont formées à partir d'atomes
et de doublets.
Elles permettent de représenter des listes, qui peuvent elles-mêmes coder des
arbres, qui peuvent représenter beaucoup de choses !. Dans tout ce qui suit
nous nous conformerons à la tradition Lispienne en utilisant l'abréviation
S-expr pour désigner les Expressions Symboliques.
2.1. Les Atomes
Les S-expressions les plus simples sont les Atomes : ce sont des entités
indivisibles (a priori) repérées par leur nom (une chaîne de caractères). On
distingue deux catégories : Les nombres (ou atomes numériques)et les symboles
(appelés aussi - allez savoir pourquoi- atomes non numériques) :
Exemples :
abcd toto + * 1789 -123456
sont six atomes, dont deux
numériques. (Exercice réservé aux mal-comprenants : trouvez lesquels ?)
Il existe un atome non-numérique qui joue un rôle particulier et que l'on
note nil ou bien () ; nous en reparlerons très bientôt.
2.2. Les doublets
Un doublet est la donnée de deux S-Expr.
Voici par exemple une représentation graphique du doublet construit à partir
des atomes A et B:
.
/ \
A B
La notation linéaire consacrée pour ce genre d'objet est :
( A . B )
Attention : ce doublet est très différent de son collègue ( B . A ) !
On utilise aussi le vocable paire pointée
pour désigner les doublets.
Remarquez bien que les parties gauches et droites d'un doublet peuvent être
également des doublets, qui eux-mêmes, etc...
Exercice : donnez une représentation graphique de l'objet décrit par la
S-expr suivante : ( ( A . ( B . C ) ) . D )
Solution
.
/ \
. D
/ \
A .
/ \
B C
Exercice : Donnez la S-expr qui décrit l'objet :
.
/ \
A .
/ \
B .
/ \
C NIL
Solution : ( A . ( B . ( C . nil ) ) )
Pour des raisons historiques, les composantes gauche et droite d'un doublet
sont appelées respectivement car et cdr de ce doublet. L'opération qui
consiste à faire un doublet à partir de deux S-expr est appelée cons.
Exercice : donnez le car du cdr du cdr de
l'expression précédente. (réponse : C).
L'ensemble des S-expressions est donc l'ensemble des objets (finis) qui sont:
-
soit des atomes;
- soit des doublets formés de deux S-expressions.
Ceci est une définition récursive bien fondée
(c'est-à-dire pas complètement
absurde) puisqu'elle définit
- d'une part les S-expr de base (les plus petites)
qui sont les atomes;
- et d'autre part qu'elle indique la manière de combiner
des expressions en une expression plus grosse (par un doublet).
Curiosités:
- Lorsqu'on fait le cons entre deux expressions e1 et e2 on obtient un
doublet. Mais à partir de ce doublet on sait retrouver les deux expressions
d'origine e1 et e2 puisque l'on a :
car [ cons[e1 ,e2 ] ] = e1
cdr [ cons[e1 ,e2 ] ] = e2
Donc l'ensemble des doublets d'expression est isomorphe au produit cartésien
de l'ensemble des expressions par lui-même :
DOUBLETS = SEXPR x SEXPR
-
Formellement, on définit SEXPR comme étant le plus petit ensemble qui soit solution
de l'équation ensembliste :
SEXPR = ATOMES u (SEXPR x SEXPR)
La précaution «le plus petit...» permet d'exclure les objets infinis de la
forme :
.
/ \
a \
/ \
a \
/ \
a \
/ \
a .
celui-ci par exemple est la solution de l'équation
X = cons [ a,X ]
2.3. Notation des Listes
Nous venons de voir une notation pour les S-expr. qui représentent en fait
des arbres binaires dans toute leur généralité. Nous allons voir comment
représenter des listes (séquences) d'objets par des S-expr ; et quelques
facilités de notation bien agréables. Mais auparavant nous rappellerons la
notion de liste.
2.3.a Définition des Listes
Pour construire des listes d'objets il faut tout d'abord avoir un ensemble
d'objets de base (par exemple OBJETS=Nombres si on veut construire des listes
de nombres) ; dès lors l'ensemble L des listes d'objets est défini par :
l est une liste si et seulement si
- l = ()
- ou l = cons[o,l'], où o est
dans Objets et l' est une liste.
Nous avons donc un objet () qui représente la liste vide, et une opération
(appelée cons, comme pour les s-expr) qui permet de fabriquer une liste l en
mettant un objet o devant une liste plus petite l'.
cons[ 5 , ( 8 5 6 2 ) ] = ( 5 8 5 6 2 ) ]
ce qui, soit dit en passant, sous-entend l'existence de deux fonctions
réciproques de cons : premier et reste, applicables aux listes non vides :
premier [ cons[o,l] ] = o
reste [ cons[o,l] ] = l
On a donc : LISTES = { () } u OBJETS x LISTES
2.3.b Représentation des Listes par des S-expr
Il est facile de représenter une liste (séquence) d'objets (par exemple la
liste formée des 4 atomes DO MI SOL et DO par un arbre binaire «en peigne» :
.
/ \
DO \
/ \
MI \
/ \
SOL \
/ \
DO nil
La notation vue plus haut nous conduirait à représenter la chose sous la
forme :
(DO . (MI . (SOL . (DO . nil))))
C'est lourd et peu commode. On adopte donc une convention plus agréable en
notant simplement la chose :
( DO MI SOL DO )
Remarques:
-
Par convention l'atome nil désigne la liste vide notée également ()
-
L'objet suivant:
.
/ \
A \
/ \
B \
/ \
C D
est noté (A B C . D), il faut prendre garde de ne pas le confondre avec la
liste à 4 éléments (A B C D) .
-
on peut avoir des listes d'expressions quelconques, qui peuvent être
éventuellement représenter des listes ...
( (DO MI SOL SI-BEMOL) (RE FA LA DO) (SOL SI RE FA) )
Une liste L est donc représentée par un doublet (dans le cas où elle n'est
pas vide bien sùr):
-
le car de ce doublet correspond au premier élément de L;
-
le cdr représentant la liste L privée de ce premier élément.
Exercices :
- Combien y a t'il d'éléments dans : ( (A B) (C D) () )
- Donnez les représentation graphiques et les notations linéaires des objets
suivants :
-
la liste (MACHIN CHOSE)
- la liste qui contient deux éléments, l'atome TRUC et la liste précedente.
- la liste à un seul élement HOUPS
- la liste qui contient seulement la liste vide.
2.4. Représentation d'arbres par des listes
Nous avons vu précédemment que les S-expr pouvaient servir à dénoter les
arbres binaires. La représentation d'arbres quelconques est aisée :
considérons par exemple les arbres :
+ * GLOUP
/|\ /|\
/ | \ / \
A B 2 2 B +
/ \
/ \
C D
A propos, qu'est ce donc qu'un arbre ? C'est :
-
soit un petit arbre, réduit à un seul symbole (exemples A, B, 2, GLOUP).
On le code alors par ce symbole.
- soit un arbre plus gros formé d'un symbole «racine» (ici +, *) et de
sous-arbres «fils».
Un arbre est représenté par une liste dont le premier élément
est la racine de l'arbre; et les suivants les sous-arbres immédiats :
( + A B 2 )
Exercice: dessiner l'arbre correspondant à :
(f (g a b) c d)
Le codage d'un arbre par une liste est en fait une fonction (appelons la «c»
que l'on peut présenter de la manière suivante :
c : Arbres -> S-expr
c[ symbole ] = symbole
c[ racine ] = ( racine c[a1 ] c[a2 ] ... c[an ] )
a1 a2 .. an
La présentation de cette fonction sous forme récursive est en fait très
naturelle : elle ne fait que suivre le schéma de définition des arbres :
- La première ligne définit le codage des «petits arbres»;
- la seconde ligne indique comment construire le codage d'un gros arbre
a partir du codage de ses sous-arbres....
Un des objectifs principaux de ce cours est de vous habituer à :
-
définir des objets par récurrence,
- définir des fonctions sur ces objets.
Mais nous aurons le temps d'y revenir...
2.5 Représentation des Programmes
2.5.1 Syntaxe Abstraite
On peut associer à tout fragment de programme (PASCAL, p.e) son arbre de
syntaxe abstraite. Par exemple l'arbre de syntaxe abstraite associé à
l'instruction :
while A>(B-2)*C
do A:=A+1
(qui ne fait rien de spécialement intéressant, le problème n'est pas là) :
boucle_ttq
/ \
> affectation
/ \ / \
A * A +
/ \ / \
- C A 1
/ \
B 2
Pourquoi «abstraite» ? Parce que cet arbre fait abstraction des problèmes
liés aux conventions de représentation écrite (syntaxe) des objets du langage
Pascal (les problèmes de point-virgules, de priorités d'opérateurs, etc...);
pour s'attacher à la structure profonde du programme.
Bref, ceci pour dire qu'un programme peut être représenté par un arbre que
l'on code par une expression symbolique !
2.5.2 Syntaxe Abstraite des Fonctions
En Lisp nous programmerons en écrivant des fonctions; voici par exemple la
fonction DOUBLE. Que fait cette fonction ? Elle prend un nombre (disons x)
et renvoie le double de sa valeur; c'est-à-dire 2*x .
Double : x ---> 2*x
Pour définir une fonction il nous faut un certain nombre d'informations :
son nom, | DOUBLE
|
les noms de ses paramêtres, | X (ici un seul)
|
le corps de la fonction; | 2*X
|
On peut décrire ceci sous forme d'un arbre :
definition
/ | \
/ \
nom liste de corps
paramêtres
En Lisp, la définition de cette fonction s'écrit :
(de DOUBLE (X) (* X 2 ) )
La définition d'une fonction est décrite par une liste à 4 éléments, dans
l'ordre :
-
le mot-clé «de» (c'est un atome ; signifiant «define»)
- le nom de la fonction (également un atome);
- une liste de paramêtres formels (liste d'atomes);
- le corps de la fonction (une s-expression)
Exercice :
Analysez la définition suivante (en devinant ce que vous ne savez pas encore):
(de COMIQUE (LAUREL HARDY)
(if ( <= LAUREL HARDY)
LAUREL
HARDY
))
Réponse :
séance TP à prévoir dès que possible:
Utilisation Pratique de Lisp
Exercice :
Ecrire une fonction MAX qui donne le maximum de deux nombres.
Exercice :
Ecrire une fonction MAX3 qui donne le plus grand parmi 3 nombres.
Exercice :
Ecrire une fonction DELTA qui calcule le discriminant associé à une
équation du second degré (encore !!) dont les coefficients sont donnés en
paramêtres.
M. BILLAUD Département Informatique IUT-A Bordeaux (1989)