TD/TP n°5 : p-listes ou listes de propriétés
1. Rappels et compléments sur les p-listes ou listes de propriétés
1.1 Définition
Tout symbole (atome littéral) est lié à une valeur dans son environnement. On appelle c-valeur, sa valeur de liaison.
Mais le langage Lisp a aussi prévu des mécanismes de liaison inspirés des bases de données et un symbole peut être lié à une liste de propriétés ou p-liste.
Une p-liste permet d'associer à un symbole plusieurs valeurs accessibles grâce à une clé, appelée encore propriété.
Par exemple, Pierre a
- un père nommé Jean
- une mère nommée Julie
- un fils nommé Gérard
On dira que la valeur des propriétés
- "père" de Pierre est Jean
- "mère" de Pierre est Julie
- "fils" de Pierre est Gérard
1.2 Les fonctions de manipulation
- Pour saisir une propriété et sa valeur associée, on utilise habituellement en Lisp, la fonction putprop. Ci-dessous, son utilisation pour définir les propriétés de Pierre :
> (putprop 'pierre 'pere 'jean)
> (putprop 'pierre 'mere 'julie)
> (putprop 'pierre 'fils 'gerard)
Malheureusement, cette fonction n'existe pas en Common Lisp et on devra la réécrire, en utilisant les suivantes (voir ci-dessous).
- Pour récupérer la valeur associée à une propriété, on utilise la fonction get. Ci-dessous, son utilisation pour notre héros Pierre :
> (get 'pierre 'pere)
jean
> (get 'pierre 'mere)
julie
> (get 'pierre 'fille)
nil
Lorsque la propriété n'a pas été définie, nil est renvoyé. Malheureusement, on ne peut pas distinguer si nil est la valeur effectivement associée à une propriété existante ou si cette propriété est absente. Pour cela, on peut donner à get, un troisième argument qui sera renvoyé si la propriété est absente :
> (get 'pierre 'fille 'inconnu)
inconnu
- Pour retirer une propriété de la p-liste, on utilise la fonction remprop. Pierre ? C'est à toi :
> (remprop 'pierre 'fils)
> (get 'pierre 'fils 'inconnu)
inconnu
- Comme précisé ci-dessus, il nous faut donc écrire la fonction putprop non définie en Common Lisp. Pour cela on affectera une valeur à l'endroit lié à une propriété donnée. Cet endroit (ou encore emplacement mémoire) s'obtient en invoquant la fonctionget, tandis qu'une liaison sur un emplacement mémoire se fera avec la fonction setf qui est une généralisation de la fonction setq, cette dernière ne permettant que de créer des liaisons sur des symboles représentant des variables. Les trois constructions de propriétés définies précédemment s'écrivent alors, sans la fonction putprop, de la manière suivante :
> (setf (get 'pierre 'pere) 'jean)
> (setf (get 'pierre 'mere) 'julie)
> (setf (get 'pierre 'fils) 'gerard)
Voici donc maintenant, la définition de cette fonction putprop qui simplifiera les constructions précédentes pour les rendre compatibles avec le début du paragraphe :
> (defun putprop (symbole propriete valeur)
(setf (get symbole propriete) valeur)
symbole)
2. Problème : gestion d'une bibliothèque
On s'intéresse dans cet exercice à la construction d'une petite base de données permettant de gérer une bibliothèque de livres. Les informations que l'on doit gérer sont les suivantes : le titre, l'auteur et une liste de mot-clés.
Exercice 1 :
La bibliothèque est représentée par une liste d'identifiants, chacun de ceux-ci dénomme un livre particulier.
Ecrire une fonction qui, à partir d'un paramètre identifiant un livre et de 3 autres paramètres correspondant aux informations à stocker sur ce livre, l'ajoute à la bibliothèque et construit pour chaque livre, une p-liste associée contenant les informations à gérer.
Exercice 2 :
Ecrire une fonction de recherche de livres, à partir du nom d'auteur. Cette fonction renverra la liste des identifiants de livres qui correspondent à cet auteur. Faire en sorte que cette fonction puisse aussi être utilisée pour une recherche sur le titre.
Exercice 3 :
Ecrire une fonction de recherche de livres, à partir d'un mot-clé. Cette fonction renverra la liste des identifiants de livres qui possèdent ce mot-clé dans leur liste correspondante.
Exercice 4 :
Il s'agit maintenant de créer des fonctions d'affichage un peu plus évoluées que de renvoyer une simple liste d'identifiants de livres. Pour cela, on aura besoin d'utiliser les fonctions d'entrées-sorties qui sont rappelées dans l'annexe ci-après.
- Ecrire une fonction qui affiche "en clair" les informations d'un livre donné en précisant son titre, son auteur et ses mots-clés.
- Réecrire les fonctions de recherche, par auteur, titre ou mot-clé, de sorte qu'elles affichent pour chaque livre correspondant au critère de recherche, son titre, son auteur et ses mot-clés.
Remarque : ces derniers affichages s'obtiendront en faisant en sorte que les fonctions renvoient une chaine de caractères contenant toutes les informations que l'on souhaitent afficher.
3. Annexes : quelques éléments sur les entrées-sorties
Lecture
La fonction read permet de saisir des données.
> (read)
Par défaut, elle attend qu'une expression lisp soit tapée au clavier et validée (touche "entrée"). Pour récupérer cette saisie, on pourra par exemple la lier grâce à setq :
> (setq a (read))
Ecriture
Plusieurs procédés peuvent être utilisés pour écrire des données, c'est à dire les afficher à l'écran par défaut.
La fonction print qui, suivie d'un paramètre, permet d'afficher la valeur de ses paramètres.
La fonction format est la fonction la plus générale. Ses paramètres sont :
- Le premier est celui de destination (écran, fichier, chaîne de caractères, ...). On retiendra notamment que si c'est t, on écrira (ou affichera) sur l'écran.
- Le second est une chaîne de caractère qui permettra d'afficher les caractères affichables. Cette chaîne peut contenir aussi des chaînes de contrôle qui décrivent où et comment écrire les paramètres optionnels suivants.
> (format t "Hello Pierre")
Hello Pierre
;; ~A indique une position à remplir par un des arguments
;; en respectant l'ordre d'apparition
> (format t "~A plus ~A égale ~A" 2 3 5)
2 plus 3 égale 5
;; ~% indique un saut de ligne
> (format t "Bonjour, ~%bonsoir")
Bonjour,
bonsoir
Utilisation pour la manipulation de chaînes de caractères
- Ecrire dans une chaîne de caractères :
Si l'argument de destination (le premier) de la fonction format est nil, l'écriture se fait dans une chaîne de caractère qui est retournée comme valeur de la fonction
> (format nil "~A est plus grand que ~A" 3 2)
"3 est plus grand que 2"
- Concaténer deux chaînes de caractères :
On peut alors définir la fonction suivante qui permet de concaténer 2 chaînes de caractères.
> (defun concat_cc (a b)
(format nil "~A~A" a b))
4. Quelques références
- H. Wertz, (Common) Lisp, une introduction à la programmation, Masson, 1989
- F. Leboutte, Introduction à la programmation en Common Lisp, www.algo.be, 2002
- S.-M. Lau, LISP Programming Tutorial Notes, CSC4510, Hong Kong University, 1996