Programmation Fonctionnelle et Symbolique
Evaluation des expressions symboliques


Résumé

Certaines S-expr codent des programmes; elles sont donc destinées à être évaluées par un évaluateur. Dans cette partie nous allons étudier en détail le fonctionnement de cet évaluateur; et passer en revue quelques fonctions prédéfinies indispensables à la programmation en Lisp. <

3.a La boucle «top-level»

Le langage Lisp est généralement implanté sous forme d'un programme interactif (interprèteur) qui répète indéfiniment (ou presque) une bouclettop-level, c'est-à dire un mécanisme qui : Analysons soigneusement le travail de l'interprèteur sur ce dernier exemple: Suggestion : On peut illustrer ceci par une imbrication de boîtes d'évaluation, chaque boite étant de la forme : +-- expression --+ | | | | | | | EVAL | | | | | | | +--- résultat ---+ Avec un certain nombre de règles : L'évaluation d'un nombre renvoie ce nombre lui-même : +---- nombre ----+ | | | | | | | EVAL | | | | | | | +---- nombre ----+ L'évaluation d'une somme (ou différence, ou produit, etc..) provoque l'évaluation de ses arguments, et le calcul de la somme (...) des résultats partiels : +----- ( + e e ) ------o | 1 2 | | | | +- e --+ +- e -+ | | | 1 | | 2 | | | | | | | | | | | | | | | +- r -+ +- r -+ | | 1 2 | | \ / | | \ / | | \ / | | additionneur | | | | | | | | V | +---------- R -----------+ L'entrée dans une boite correspond à un appel au module évaluateur.

Exercice: tracer la boite pour l'évaluation de l'expression dont le calcul a été détaillé plus haut :

( - ( * 8 4 ) ( + 15 16 ) )

3.b Notion d' Environnement

En Lisp les atomes (non-numériques) peuvent jouer le rôle de variables, c'est-à-dire que l'on peut : L'association d'une valeur à une variable se fait notamment lors d'un appel de fonction. Considérons par exemple la fonction :
(de FOO (X Y Z) ( + X (* Y Z ) ) )
Lorsque l'on écrit ceci les trois atomes X, Y et Z n'ont pas de valeur particulière, puisque ce sont des variables muettes (des paramêtres). En revanche, lorsqu'on appelle la fonction FOO, p.ex (FOO (+ 3 1) 2 1)) on leur fournit des valeurs : X vaut 4, Y vaut 2 et Z vaut 1. On doit ensuite évaluer le corps de la fonction dans cet environnement.

La valeur d'une variable dans un environnement donné s'obtient simplement par évaluation de l'atome (symbole) qui représente cette variable :

+-- x :symbole --+ | | | | | | | | | | | | | | | V | | | +-- valeur(x) ---+ Exemple : Calcul de (FOO 2 3 4) : +------ (FOO 2 3 4) -----------------------o | | | X=2 Y=3 Z=4 | | | | | | +----- ( + X (* Y Z) ) -----------+ | | | | | | | | | | | | | | | +- Y -+ +--- ( * Y Z) -----+ | | | | | | | | | | | | | | | +- Y -+ +- Z -+ | | | | | | | | | | | | | | | | | +- 2 -+ | | | | | | | | | | | | | | | | | | | | | | +- 3 -+ +- 4 -+ | | | | | | \ / | | | | | | | \ / | | | | | | * | | | | | | | | | | | | | | | | | | | | | V | | | | | | | | | | | | +------- 12 -------+ | | | | | | | | \ / | | | | / | | | | \ / | | | | \ / | | | | + | | | | | | | | | | | | | V | | | | | | | +------------- 14 ----------------+ | | | | | +----------------- 14 ---------------------+ Attention : un environnement peut en cacher un autre ! Considérons par exemple la fonction
(de BLOUP ( X Y ) (+ (FOO 2 X Y) (FOO 11 12 X)))
et l'appel (BLOUP 5 6).

3.c La Fonction «Quote»

Nous avons proclamé dans l'introduction que Lisp permettait de manipuler des expressions symboliques sous formes de S-expr. Or nous n'avons présenté pour l'instant que des expressions renvoyant des résultats numériques.

Un exemple concret : nous voulons chercher le premier élement de la liste (+ X Y). Premier essai :

?- (CAR (+ X Y)) :: erreur : X : variable non définie. Evidemment, puisque l'interprèteur Lisp tente d'évaluer la S-expr (+tXtY)qui contient deux atomes dont la valeur n'est pas définie. Eussent-ils été définis (par exemple évaluation de la même expression dans un environnement où x=2 et y=1), que le résultat 5 ne nous aurait pas convenu (puisque nous voulons récupérer l'atome «+»).

Il faut donc indiquer à l'évaluateur qu'il ne doit pas évaluer la forme -synonyme de S-expr- (+ x y) , qui doit être protégée de l'évaluation par la fonction prédéfinie quote (to quote = citer). Deuxième essai:

?- (quote ( + x y ) ) -> ( + x y ) ?- (car (quote (+ x y))) -> + ; ‡à marche ! En résumé : l'évaluation d'une S-expr de la forme (quote Expr) renvoie le premier argument Expr sans l'évaluer. La boite correspondante : +- (quote expr) -+ | | | | | | | | | | | | | | | V | | | +----- expr -----+ L'usage de telles expressions est très fréquent, aussi il existe une facilité de notation sans laquelle la vérification du nombre de parenthèses d'une expression serait un vrai cauchemar : (quote Expr) est noté
'Expr
Dans notre exemple, on écrira donc de préférence :
(car '(+ x y)))

3.d Manipulation élémentaire des Listes

Les 3 opérations fondamentales sur les doublets sont : Les fonctions correspondantes s'appellent cons (à deux paramêtres), car et cdr (à un paramêtre). Comme les fonctions arithmétiques, elles évaluent leurs paramêtres.

Exemples/Exercices:

?- (cons 'laurel 'hardy)) -> (laurel . hardy) ?- (cons 'laurel (cons 'hardy nil)) -> (laurel hardy) ?- '(laurel hardy) -> (laurel hardy) ?- (car '(laurel hardy)) -> laurel ?- (cdr '(laurel hardy)) -> hardy Exercices: Il existe un certain nombre de «fonctions d'accès» qui sont des compositions de car et cdr : cadr, cdar, caar, cddr, caddr, cdddr, etc... Leur signification est évidente, (caddr X) par exemple représente :
(car (cdr (cdr X)))
Exercice : Quelles fonctions représentent l'accès au premier, second, troisième élément d'une liste ? Au premier élement du second élément d'une liste de listes ? (Réponses : car, cadr, caddr , caadr).

3.e Les Prédicats

On appelle prédicats les fonctions qui fournissent un résultat «booléen», vrai ou faux. Il est clair que nous en aurons besoin dès que nous aborderons la programmation en Lisp. La convention adoptée pour la représentation des booléens est la suivante : Bien entendu, il y a des prédicats arithmétiques ; par exemple les comparaisons = < > <= >= etc...: ?- ( = 1 2 ) -> () ?- ( < 1 2 ) -> T Attention à l'ordre des paramêtres ! (< 2 X) signifie « 2 est il inférieur à X ?» (et non le contraire)

Les S-expr. sont un mélange de nombres, d'atomes et de doublets. On dispose de prédicats pour déterminer le «type» des objets :

On dispose également de deux prédicats de comparaison de S-Expr : eq et equal. La différence entre les deux prédicats repose sur des considérations techniques d'implémentation des doublets : ce n'est pas tout à fait la même chose : ?- (eq (cons 1 (cons 2 3)) (cons 1 (cons 2 3))) -> T ?- (equal(cons 1 (cons 2 3)) (cons 1 (cons 2 3))) -> () Pourquoi ? dans les deux cas on fabrique deux expressions -à des emplacements distincts en mémoire- qui ont cependant la même structure. eq teste l'identité, equal la similarité.

Remarque : Les nombres et les atomes n'existent qu'en un seul exemplaire en mémoire : la comparaison par eq ou equal fournit dans ce cas forcément le même résultat.

On dispose également des opérateurs booléens and, or et not, avec la signification habituelle. On notera cependant que les opérateurs and et or sont évalués en 'court-circuit'; par exemple dans l'expression :

(or (= X 0) (= (/ 1 X) Y) )
il ne peut y avoir de division par zéro.

3.f Structures de Contrôle Fondamentales

Dans tout langage de programmation on appelle «structure de contrôle» tout mécanisme permettant de combiner plusieurs actions en une action plus complexe. Question : Citez les structures de contrôle de vos langages favoris.

Réponse : Ne pas oublier que l'enchaînement de deux instructions est aussi une structure de contrôle ! Ici, nous programmons avec des fonctions; c'est-à-dire que nous composons entre elles des expressions, en leur appliquant des fonctions - pour l'instant prédéfinies, mais çà ne durera pas. Par conséquent, la première structure de contrôle de Lisp est l'application d'une fonction a des arguments; structure sous-jacente dans tout ce que nous avons vu.

L'alternative

La fonction cond réalise une alternative multiple; présentée sous la forme :

(cond Clause1 Clause2 ... Clausen)
Où chaque clause est une liste dont le premier élément représente une condition, et la suite la liste des choses à évaluer si la condition s'avère satisfaite.

Exemple :

(cond ((number X) «C'est un entier») ((stringp X) «C'est une chaîne») ((null X) «C'est nil») ((atom X) «C'est atomique») (T «Un doublet»)) Remarques :

  1. L'évaluation des conditions se fait dans l'ordre des clauses, et s'arrête à la première condition vraie.
  2. Si aucune condition n'est satisfaite, l'évaluation d'une forme (cond ...) renvoie nil.
  3. On utilise fréquemment l'atome T (true) comme «attrape-tout» pour la dernière clause (clause sinon).
Exemple : Ecrire une fonction qui calcule le maximum parmi trois nombres : (de M3 (N P Q) (cond ( (and (>= N P) (>= N Q)) N) ( (and (>= P N) (>= P Q)) P) ( T Q) )) Exercice:

Ecrire une fonction F qui renvoie la différence entre le plus grand et le plus petit nombre parmi trois :

?- (F 5 13 6) -> 8 (6 cas) Remarques :