Cours 8

Programming, Mathematics, Common Lisp · course

Voir tous les documents en programmation

**Cours 8**

**Contenu**

* Hiérarchie des nombres, nombres complexes * Début de la programmation impérative: affectation (setf, psetf) * affectation de places (valeurs gauches/valeurs droites) * Fonctions desctructives sur les listes: nconc, nreverse, delete * Valeurs multiples (values, multiple-value-bind, multiple-value-list, multiple-value-call) * tables de hachage (make-hash-table, gethash)

Les transparents 71--76

Listes pour représenter des piles

Une liste est une excellente façon de représenter une pile. Pour obtenir le sommet de la pile, il suffit d'utiliser la fonction *car*, pour empiler un élément, on utilise *cons* et pour dépiler *cdr*.

Ces opérations sont *sans effet de bord*. Empiler un objet avec *cons* implique la *création d'une nouvelle pile* mais avec un élément de plus.

Parfois, il est souhaitable de modifier une variable contenant une pile. On obtient alors souvent des *idiomes* comme :

(setf \*pile\* (cons obj \*pile\*)) (setf \*pile\* (cdr \*pile\*)

pour empiler ou dépiler une pile, ici valeur de la variable *\*pile\**. Les deux*macros* Common Lisp *push* et *pop* capturent ces idiomes :

\* (defparameter \*pile\* '())

\*PILE\* \* (push 'a \*pile\*)

(A) \* (push 'b \*pile\*)

(B A) \* (pop \*pile\*)

B \* \*pile\*

(A) \* (pop \*pile\*)

A \* \*pile\*

NIL \*

Les deux macros *push* et *pop* sont impossibles à écrire sous forme de fonctions, car aucune fonction n'est capable de modifier la valeur d'une variable donnée en argument.

Nombres

**Sous-sections**

* [Les différents types de nombres](file:///E%3A%5C%5CIrene_Book%5C%5Cnode67.html)

* + [Entiers](file:///E%3A%5C%5CIrene_Book%5C%5Cnode68.html)

* + [Ratios](file:///E%3A%5C%5CIrene_Book%5C%5Cnode69.html)

* + [Flottants](file:///E%3A%5C%5CIrene_Book%5C%5Cnode70.html)

* + [Complexes](file:///E%3A%5C%5CIrene_Book%5C%5Cnode71.html)

* [Prédicats](file:///E%3A%5C%5CIrene_Book%5C%5Cnode72.html)

* [Comparaisons](file:///E%3A%5C%5CIrene_Book%5C%5Cnode73.html)

* [Opérations arithmétiques simples](file:///E%3A%5C%5CIrene_Book%5C%5Cnode74.html)

* [Trigonométrie](file:///E%3A%5C%5CIrene_Book%5C%5Cnode75.html)

* [Trigonométrie hyperbolique](file:///E%3A%5C%5CIrene_Book%5C%5Cnode76.html)

* [Logarithmes et exponentielles](file:///E%3A%5C%5CIrene_Book%5C%5Cnode77.html)

* [Opérations bit à bit](file:///E%3A%5C%5CIrene_Book%5C%5Cnode78.html)

* [Conversion, troncature, arrondi](file:///E%3A%5C%5CIrene_Book%5C%5Cnode79.html)

Les différents types de nombres

Common Lisp propose plusieurs types de nombres, certains similaires à ceux d'autres langages, d'autres spécifiques à Common Lisp. Les nombres Common Lisp sont organisés dans une arborescence dont le sommet est number.

| | | --- | | ![\begin{figure}\begin{center} \input arborescence-nombres.pstex_t \end{center} \end{figure}](data:image/png;base64...) | | **Figure 6.1:** Arborescence des types de nombres |

Il y a deux sous-types du type number : real et complex. Contrairement à ce qui est habituel en mathématique, ces deux types sont disjoints, la raison étant que la représentation en mémoire des réels et des complexes n'est pas la même.

**Sous-sections**

* [Entiers](file:///E%3A%5C%5CIrene_Book%5C%5Cnode68.html)

* [Ratios](file:///E%3A%5C%5CIrene_Book%5C%5Cnode69.html)

* [Flottants](file:///E%3A%5C%5CIrene_Book%5C%5Cnode70.html)

* [Complexes](file:///E%3A%5C%5CIrene_Book%5C%5Cnode71.html)

Entiers

Les entiers Common Lisp sont différents des entiers des autres langages car leur taille n'est pas limitée. Il n'y a pas de limitation concernant le nombre de bits dans la représentation d'un entier. La seule limitation est la taille de la mémoire. Exemple :

\* (expt 1234 20)

67035243914691711794360082394262505332216263021359359696306176 \*

Le type integer est un sous-type du type rational. Il y a deux sous-types du type integer : fixnum et bignum. Les fixnums sont les entiers dont la valeur absolue est suffisamment petite pour qu'on puisse la représenter dans un mot de l'ordinateur.

La norme exige qu'un fixnum soit représenté sur au moins 16 bits, mais le nombre exact dépend de l'implémentation. Il est possible de tester l'intervalle des fixnums en utilisant les deux constantes globales most-positive-fixnum et most-negative-fixnum. Exemple :

\* most-positive-fixnum

536870911 \* most-negative-fixnum

-536870912 \*

Pour déterminer le nombre de bits correspondant, il suffit de calculer le logarithme (en base 2) de ces nombres. La version de CMUCL sur la famille de processeurs Pentium donne le résultat suivant :

\* (log (- most-negative-fixnum) 2)

29.0 \*

Le nombre de bits utilisés ici (29) peut paraître un peu étrange, mais s'explique facilement par la représentation interne des objets Common Lisp et en particulier des fixnums.

Les bignums sont les entiers qui nécessitent plus de bits que les fixnums. La conversion entre les deux est automatique. Si le résultat d'un calcul est un entier dont la valeur absolue est assez petite pour être représentée sous forme de fixnum, le résultat sera toujours un fixnum. Le système CMUCL contient deux prédicats supplémentaires par rapport à la norme : fixnump et bignump. Ces prédicats nous permettent de tester la représentation.

Common Lisp est aussi capable de manipuler des entiers représentés avec un nombre quelconque fixé de bits. Un tel entier est appelé un byte (qui ne doit donc pas être traduit par octet). Il est par exemple possible de créer un tableau dont le contenu est une suite d'entiers sur 21 bits. Certains de ces bytes ont souvent une repésentation efficace, en particulier ceux de 1, 8, 16 et 32 bits.

Ratios

Les ratios forment un type de nombres qui n'existe dans pratiquement aucun autre langage. Il s'agit du type ratio qui est un sous-type de rational.

Les ratios permettent de représenter le résultat d'un grand nombre de calculs de façon exacte. Ce sont des calculs dont les données initiales sont des rationnels (en particulier des entiers) et dont les opérations sont uniquement addition, soustraction, multiplication et division.

Un exemple de ce type de calcul est la résolution de systèmes d'équations linéaires à coefficients rationnels. Ce type de calcul nécessite parfois la soustraction entre deux nombres de différents ordres de grandeur. Si les flottants sont utilisés pour effectuer un tel calcul, on risque de perdre une grande partie de la précision dans les calculs intermédiaires, ce qui peut donner un résultat final erroné et donc inexploitable

Flottants

Le type float est un sous-type de real mais n'est pas un sous-type de rational, même si les flottants forment en fait un sous-ensemble des rationnels. Les deux types float et rational sont en fait distincts.

Comme indiqué dans la section [4.1.1](file:///E%3A%5CIrene_Book%5Cnode45.html#exprflottants), il y a quatre sous-types du type float : short-float, single-float, double-float et long-float.

Les opérations dont le résultat est un flottant (fonctions trigonimétriques, logarithmes, etc) doivent choisir le sous-type exact de float parmi ceux disponibles. La règle Common Lisp est simple : le type exact du résultat est le même que le type le plus précis des arguments. Exemple :

\* (expt 2.4 4.5)

51.398525 \* (expt 2.4d0 4.5)

51.39851690685247d0 \* (expt 2.4 4.5d0)

51.39852609762414d0 \*

Complexes

Le type complex est un sous-type du type number. Comme cela a déjà été indiqué dans la section [4.1.1](file:///E%3A%5CIrene_Book%5Cnode46.html#exprcomplexes), il y a deux sous-types du type complex selon le type des composants. En Common Lisp, ces deux types s'appellent (complex rational) et (complex float). On remarque que les noms de ces deux types ne sont pas des symboles, mais des listes dont le premier élément est complex et le deuxième le nom d'un sous-type de real. En fait, le type complex est paramétrable avec un type réel qui indique le type des deux parties. Un type dont le nom est un symbole est appelé type atomique (en Anglais : atomic type) et un type dont le nom est paramétré est appelé type composite (en Anglais : compound type).

**Prédicats**

Publicité

Common Lisp étant un langage dynamiquement typé, il est souvent nécessaire de tester le type d'un objet. Ceci se fait grâce à des *prédicats de type*. Un prédicat est une fonction dont la valeur de retour est booléenne. Voici les prédicats de type qui s'appliquent aux nombres :

| | | | --- | --- | | nom | fonction | | *numberp* | pour tester si un objet est un nombre | | *realp* | pour tester si un objet est un nombre réel | | *complexp* | pour tester si un objet est un nombre complexe | | *rationalp* | pour tester si un objet est rationnel | | *integerp* | pour tester si un objet est un entier | | *floatp* | pour tester si un objet est un flottant | | *zerop* | pour tester si un nombre est égal à *0* | | *plusp* | pour tester si un nombre est positif | | *minusp* | pour tester si un nombre est négatif | | *evenp* | pour tester si un entier est pair | | *oddp* | pour tester si un entier est impair |

Chacun des prédicats *numberp*, *realp*, *complexp*, *rationalp*, *integerp* et *floatp* s'applique à un objet Common Lisp arbitraire, alors que *zerop*, *plusp* et *minusp* nécessitent un nombre et *evenp* et *oddp* un entier.

**Comparaisons**

Les nombres réels sont *totalement ordonnés*. Les opérations de comparaison permettent de tester l'ordre entre deux ou plusieurs nombres. Les nombres complexes ne sont pas totalement ordonnés, mais les tests d'égalité et inégalité existent.

Voici les opérations de comparaison en Common Lisp :

| | | | --- | --- | | nom | fonction | | *=* | égal | | */=* | inégal | | *<* | inférieur | | *<=* | inférieur ou égal | | *>* | supérieur | | *>=* | supérieur ou égal |

Contrairement à d'autres langages qui exigent exactement deux opérandes pour ces opérations, Common Lisp généralise ces opérations à un nombre arbitraire (supérieur à zéro) d'opérandes.

Voici la définition de ces opérations :

| | | | --- | --- | | nom | fonction | | *=* | *vrai* si et seulement si tous les opérandes sont égaux | | */=* | *vrai* si et seulement si tous les opérandes sont différents | | *<* | *vrai* si et seulement si les opérandes sont strictement croissants | | *<=* | *vrai* si et seulement si les opérandes sont croissants | | *>* | *vrai* si et seulement si les opérandes sont strictement décroissants | | *>=* | *vrai* si et seulement si les opérandes sont décroissants |

Par définition, l'ensemble de ces opérations donne *vrai* si un seul argument a été fourni. Exemples :

\* (= 1.0)

T \* (= 1.0 1)

T \* (= 1.0 2.0)

NIL \* (= 4/2 2 #c(2.0 0))

T \* (< 1/3 0.5 3/2 1.04)

NIL \* (< 1/3 0.5 3/2 1.9)

T \* (> 12 14/2 7 1/5 0.002)

NIL \* (> 12 15/2 7 1/5 0.002)

T \* (> 12 14/2 7 1/5 0.002)

NIL \* (>= 12 14/2 7 1/5 0.002)

T \*

Alors que d'autres langages traitent les opérateurs de comparaison comme éléments syntaxiques spéciaux, en Common Lisp il s'agit de fonctions normales dont les noms sont les symboles *=*, */=*, *<*, *<=*, *>* et *>=*. Ceci est important, car il devient alors possible de passer l'une de ces fonctions en argument à d'autres fonctions (voir chapitre [7](file:///E%3A%5CIrene_Book%5Cnode87.html#chapfonctions)).

**Opérations arithmétiques simples**

Comme d'autres langages, Common Lisp a des opérations d'addition, soustraction, mulitplication et division. Mais comme pour les opérations de comparaison, il s'agit en Common Lisp de fonctions normales dont les noms sont les symboles suivants :

| | | | --- | --- | | nom | fonction | | *+* | addition | | *-* | soustraction | | *\** | multiplication | | */* | division |

La fonction d'addition prend un nombre arbitraire de paramètres. Elle renvoie la somme des arguments, ou *0* si aucun argument n'a été fourni. Les arguments peuvent être de sous-type arbitraire du type *number*, y compris *complex*. Exemples :

\* (+)

0 \* (+ 123)

123 \* (+ 12 #c(3.1 9))

#C(15.1 9.0) \* (+ 12 #c(3.1 9d0))

#C(15.099999904632568d0 9.0d0) \* (+ 1 2 3 4 5 6 7 8 9 10)

55 \*

La fonction de soustraction prend au moins un argument. Si un seul argument est fourni, cette fonction renvoie l'opposé de celui-ci. Si le nombre d'arguments fournis est supérieur à un, alors la fonction de soustraction renvoie la différence entre le premier et la somme des autres. Exemples :

\* (- 234)

-234 \* (- #c(2345 22/3))

#C(-2345 -22/3) \* (- 33 2 5 3 4 1.4)

17.6 \* (- 33 2 5 3 4 1.4d2)

-121.0d0 \* (- 33 2 5 3 4 1.4d1)

5.0d0 \* (- 33 2 5 3 4 1.41d1)

4.9d0 \*

Comme l'addition, la fonction de multiplication prend un nombre arbitraire d'arguments. Elle renvoie le produit des arguments, ou *1* si aucun argument n'a été fourni. Exemples :

\* (\*)

1 \* (\* 123/11)

123/11 \* (\* 123/11 12.33)

137.87181 \* (\* 123/11 12.33 #c(33 11))

#C(4549.7695 1516.59) \*

Comme la soustraction, la fonction de divison prend au moins un argument. Si un seul argument est fourni, cette fonction renvoie l'inverse de celui-ci. Si le nombre d'arguments fournis est supérieur à un, alors elle renvoie le premier argument divisé par le produit des autres. Exemples :

\* (/ 2345)

1/2345 \* (/ #c(22 4.8))

#C(0.04338908 -0.009466709) \* (/ 12 3.0)

4.0 \* (/ 112 2 8.0 2d1)

0.35d0 \*

Les fonctions *max* et *min* prennent au moins un argument. Les arguments doivent être de type *real*. Exemples :

\* (max 4)

4 \* (max 9 46/5 9.01)

46/5 \* (min 9 46/5 9.01)

9 \*

**Trigonométrie**

Common Lisp propose une gamme complète de fonctions trigonométriques :

| | | | --- | --- | | nom | fonction | | *sin* | sinus de l'argument en radians | | *cos* | cosinus de l'argument en radians | | *tan* | tangent de l'argument en radians |

Ces fonctions sont similaires à leurs homologues d'autres langages de programmation, la différence principale étant que les fonctions Common Lisp donnent aussi un résultat si l'argument est un nombre complexe et que la précision du résultat dépend de la précision de l'argument. Exemples :

\* (sin 1)

0.84147096 \* (sin 1.0)

0.84147096 \* (sin 0.1d1)

0.8414709848078965d0 \* (sin #c(1.0 2.0))

#C(3.1657784 1.9596009) \* pi

3.141592653589793d0 \* (cos pi)

-1.0d0 \* (tan (/ pi 4))

0.9999999999999999d0 \*

Voici les fonctions trigonométriques inverses proposées par Common Lisp :

| | | | --- | --- | | nom | fonction | | *asin* | inverse de sinus | | *acos* | inverse de cosinus | | *atan* | inverse de tangent |

Ces fonctions *asin* et *acos* renvoient un résultat complexe si la valeur absolue de l'argument est supérieure à 1 ou si l'argument est complexe. La fonction *atan* renvoie un résultat complexe si l'argument est complexe :

Publicité

\* (asin 0.44)

0.45559868 \* (asin 1.0)

1.5707964 \* (asin 1.1)

#C(1.5707964 -0.4435683) \* (acos -34)

#C(3.1415927 -4.219291) \* (acos -34d0)

#C(3.141592653589793d0 -4.219291372012082d0) \* (atan 2320934029384029384029384023984502938)

1.5707964 \* (atan #c(1.0 2.0))

#C(1.3389726 0.4023595) \*

**Trigonométrie hyperbolique**

Les fonctions trigonométriques hyperboliques suivantes existent :

| | | | | --- | --- | --- | | nom | fonction | définition | | *sinh* | sinus hyperbolique | ![$\frac{e^x - e^{-x}}{2}$](data:image/png;base64...) | | *cosh* | cosinus hyperbolique | ![$\frac{e^x + e^{-x}}{2}$](data:image/png;base64...) | | *tanh* | tangent hyperbolique | ![$\frac{e^x - e^{-x}}{e^x + e^{-x}}$](data:image/png;base64...) | | *asinh* | sinus hyperbolique inverse | ![$\texttt{log}(x+\sqrt{1+x^2})$](data:image/png;base64...) | | *acosh* | cosinus hyperbolique inverse | ![$2\texttt{log}(\sqrt{\frac{x+1}{2}} + \sqrt{\frac{x-1}{2}})$](data:image/png;base64...) | | *atanh* | tangent hyperbolique inverse | ![$\frac{\texttt{log}(1+x) - \texttt{log}(1-x)}{2}$](data:image/png;base64...) |

**Logarithmes et exponentielles**

Les fonctions logarithmiques et exponentielles existent :

| | | | --- | --- | | nom | fonction | | *log* | logarithme | | *exp* | exponentielle naturelle | | *expt* | exponentielle d'une base arbitraire |

La fonction *log* calcule le logarithme népérien, mais accepte aussi un deuxième argument facultatif indiquant la *base*. Exemples :

\* (exp 1)

2.7182817 \* (exp -1)

0.36787945 \* (exp (\* #c(0 1) pi))

#C(-1.0d0 1.2246063538223773d-16) \* (expt 2 30)

1073741824 \* (log 3)

1.0986123 \* (log (expt 10 9) 2)

29.897354 \*

**Opérations bit à bit**

Les entiers Common Lisp sont codés en utilisant la repésentation binaire en *complément à 2*. On peut voir un entier comme une suite infinie de *bits*. Si le nombre est positif, le nombre est précédé d'une suite infinie de bits *0* et s'il est négatif, d'une suite infinie de bits *1*. Voici les fonctions qui exploitent cette représentation :

| | | | --- | --- | | nom | fonction | | *logand* | *et* entre les arguments | | *logandc1* | *et* entre complément du premier et deuxième argument | | *loganc2* | *et* entre premier argument et complément du deuxième | | *logeql* | *équivalence* entre les arguments | | *logior* | *ou inclusif* entre les deux arguments | | *lognand* | complément du *et* entre les arguments | | *lognor* | complément du *ou inclusif* entre les arguments | | *lognot* | complément de l'argument | | *logorc1* | *ou inclusif* entre complément du premier et deuxième argument | | *logorc2* | *ou inclusif* entre premier argument et complément du deuxième | | *logxor* | *ou exclusif* entre les arguments | | *logbitp* | vrai si un bit est *1* dans l'argument | | *logcount* | nombre de bits à *1* ou à *0* dans l'argument | | *logtest* | vrai si des bits sont à *1* |

Conversion, troncature, arrondi

Plusieurs fonctions existent pour la troncature et l'arrondi. Ces fonctions prennent toutes deux arguments : un *nombre* ![$n$](data:image/png;base64...)et un *diviseur* ![$d$](data:image/png;base64...). Le diviseur est facultatif et sa valeur par défaut est *1*. Chacune de ces fonctions renvoie *deux* valeurs (voir chapitre [13](file:///E%3A%5CIrene_Book%5Cnode158.html#chapmultiplevalues)), un *quotient* ![$q$](data:image/png;base64...)et un *reste* ![$r$](data:image/png;base64...)tels que ![$qd + r = n$](data:image/png;base64...). La valeur ![$q$](data:image/png;base64...)est obtenue par une division entre ![$n$](data:image/png;base64...)et ![$d$](data:image/png;base64...), suivie d'une conversion du résultat en entier. La différence entre ces fonctions réside dans la façon dont est faite cette conversion. Voici les fonctions disponibles :

| | | | --- | --- | | nom | fonction | | *floor* | résultat tronqué vers l'infini négatif | | *ceiling* | résultat tronqué vers l'infini positif | | *truncate* | résultat tronqué vers zéro | | *round* | résultat arrondi vers l'entier le plus proche |

Exemples :

\* (floor 234/11 5.4)

3 5.072727 \* (floor -234/11 5.4)

-4 0.3272729 \* (ceiling 234/11 5.4)

4 -0.3272729 \* (ceiling -234/11 5.4)

-3 -5.072727 \* (truncate 234/11 5.4)

3 5.072727 \* (truncate -234/11 5.4)

-3 -5.072727 \* (round 234/11 5.4)

4 -0.3272729 \* (round -234/11 5.4)

-4 0.3272729 \* (floor 233/3)

77 2/3 \* (round 234.444)

234 0.44400024 \*

Les fonctions *ffloor*, *fceiling*, *ftruncate* et *fround* sont similaires aux fonctions ci-dessus, à la seule différence que la valeur ![$q$](data:image/png;base64...)est un flottant au lieu d'un entier :

\* (ffloor 23 5)

4.0 3 \* (fround -234/33 2/5)

-18.0 6/55 \* (fceiling 44.234d0 3)

15.0d0 -0.7659999999999982d0 \* (ftruncate 9384593847593847593857938475938579385 3)

3.1281978e+36 1 \*

Pour convertir un nombre rationnel en flottant, ou un flottant en complexe, utiliser *coerce* :

\* (coerce 234 'float)

234.0 \* (coerce 234 'double-float)

234.0d0 \* (coerce 234/5 'double-float)

46.8d0 \* (coerce 2.234 'complex)

#C(2.234 0.0) \*

**Symboles**

**Sous-sections**

* [Prédicats sur les symboles](file:///E%3A%5C%5CIrene_Book%5C%5Cnode81.html)

* [Variables lexicales et spéciales](file:///E%3A%5C%5CIrene_Book%5C%5Cnode82.html)

* [mots-clés](file:///E%3A%5C%5CIrene_Book%5C%5Cnode83.html)

* [Autres utilisations des symboles](file:///E%3A%5C%5CIrene_Book%5C%5Cnode84.html)

**Prédicats sur les symboles**

Les symboles représentent un type de donnée unique en Common Lisp. Une caractéristique essentielle des symboles est qu'ils sont uniques, à savoir deux suites de caractères identiques dans le même programme (et dans le même *paquetage*), représentent *le même symbole*. Ceci n'est pas vrai pour les chaînes de caractères, par exemple. Pour les symboles, c'est une qualité essentielle, car en Common Lisp, ils sont utilisés pour nommer des variables et le programmeur est habitué à ce que deux occurrences du même nom de variable référencent la même variable.

Pour tester si un objet Common Lisp est un symbole, utiliser le prédicat *symbolp* :

\* \*print-level\*

NIL \* (symbolp \*print-level\*)

T \* \*standard-output\*

#<Synonym Stream to SYSTEM:\*STDOUT\*> \* (symbolp \*standard-output\*)

NIL \* (symbolp '\*standard-output\*)

T \*

**Variables lexicales et spéciales**

Il y a deux types de variables en Common Lisp : *variables lexicales* et *variables spéciales (ou dynamiques)* (en Anglais : lexical variables et special variables) . Les variables lexicales en Common Lisp sont similaires aux variables d'autres langages de programmation. Une telle variable peut être créée par un *let*. Les paramètres d'une fonction sont des variables lexicales. Pour des raisons d'efficacité, il n'est pas possible de manipuler la liaison entre un symbole représentant une variable lexicale et sa valeur. Les seules opérations permises sont la récupération et l'affectation de la valeur. Pour ces deux opérations, le nom de la variable est *connu par le compilateur* ce qui permet à ce dernier de générer du code efficace. La portée d'une variable lexicale est limitée à une partie du programme déterminée seulement par son texte. Exemple :

\* (let ((x 10))     (defun f (y)       (+ x y)))

F \* (defun g (x)     (f x))

G \* (g 3)

13 \*

Publicité

Ici, il y a deux variables différentes dont le nom est *x* : la première est créée par le *let* et référencée dans *f* et la deuxième est créée implicitement quand *g* est invoquée et référencée dans le corps de *g*.

Les variables spéciales, par contre, sont très différentes des variables lexicales. La portée d'une variable spéciale n'est pas limitée au texte du programme. Une telle variable est créée par *defparameter* (entre autres). Voici le même exemple avec une variable spéciale :

\* (defparameter x 5)

X \* (let ((x 10))     (defun f (y)       (+ x y)))

F \* (defun g (x)     (f x))

G \* (g 3)

6 \* x

5 \*

Ici, il y a une seule variable *x*. Elle est spéciale car elle est créée par *defparameter*. Lorsque *f* est définie, cette variable aura la valeur *10* comme dans l'exemple précédent. Mais l'appel de *g* avec l'argument *3* modifie la valeur de la variable *x* *pour la durée de l'exécution de g*, même pendant l'appel à *f*.

Les variables spéciales se comportent d'une façon tellement différente des variable lexicales que les programmeurs Common Lisp ont décidé d'avertir un lecteur éventuel du programme de la présence d'une variable spéciale : la convention est d'entourer le nom d'une variable spéciale par des astérisques comme pour \*print-level\* ou \*read-base\*.

L'utilisation de variables spéciales est une technique de programmation importante en Common Lisp. L'utilisation la plus fréquente est d'éviter le passage d'un argument à une fonction dont la valeur est presque toujours la même, mais cependant susceptible de changer. Un exemple classique de ce type d'utilisation est illustré par les variables *\*read-base\** et *\*print-base\**. Celles-ci déterminent la base à utiliser pour lire et afficher les nombres. Cette base est presque toujours *10*, mais il peut arriver qu'on ait besoin de la changer. Sans variable spéciale, on a deux possibilités pour changer le comportement des fonctions de lecture et d'affichage. La première est d'utiliser une variable globale comme ceci :

\* \*print-base\*

10 \* (print 11)

11 11 \* (setf \*print-base\* 8)

10 \* (print 11)

13 13 \* (setf \*print-base\* 10)

10 \* (print 11)

11 11 \*

Dans cette exemple, *\*print-base\** est affectée à *8* pour la durée d'un calcul (ici le calcul trivial *11*), puis à nouveau affectée à *10*. C'est une méthode peu pratique, en particulier si le calcul risque de provoquer une erreur, auquel cas la deuxième affectation ne sera pas effectuée (à moins de compliquer le code).

Dans l'exemple ci-dessus, on remarque que l'évaluation de l'appel à *print* donne *deux* lignes d'affichage de la part du système : la première est l'*effet de bord* de *print* dont le résultat est d'afficher la valeur et la deuxième est la *valeur* de l'expression *(print 11)*, en l'occurrence la même que l'argument donné à *print*.

La deuxième possibilité est d'écrire les fonctions de lecture et d'affichage avec un paramètre supplémentaire indiquant la valeur de la base ; ceci introduit beaucoup de code supplémentaire, souvent le même à chaque fois.

Avec les variables spéciales, il est possible de changer temporairement une valeur pendant la durée d'un calcul, sans crainte d'erreur :

\* \*print-base\*

10 \* (print 11)

11 11 \* (let ((\*print-base\* 8)) (print 11))

13 11 \* \*print-base\*

10 \* (print 11)

11 11 \*

Dans cette exemple, on remarque les lignes :

\* (let ((\*print-base\* 8)) (print 11))

13 11

La valeur de la variable spéciale *\*print-base\** est *8* pendant l'exécution de la fonction *print*. La première ligne d'affichage donne donc 13. La deuxième ligne d'affichage (11) représente la valeur de l'expression (let ((\*print-base\* 8)) (print 11)) entière. Cette valeur est *11* et lorsque cette valeur est affichée, la valeur de *\*print-base\** est redevenue *10*. Ici, même si le corps du *let* provoque une erreur, la valeur de *\*print-base\** redeviendra *10*.

Pour les variables spéciales, il est possible de manipuler explicitement la correspondance entre symboles et valeurs. La fonction *symbol-value* permet d'accéder à la valeur d'un symbole et de la changer avec *setf* :

\* (symbol-value '\*print-base\*)

10 \* (setf (symbol-value '\*print-base\*) 8)

10 \* 11

13 \* (defparameter \*sym\* '\*print-base\*)

\*SYM\* \* \*sym\*

\*PRINT-BASE\* \* (symbol-value \*sym\*)

10 \* (setf (symbol-value \*sym\*) 10)

10 \* 11

11 \*

**mots-clés**

Un symbole dont le nom commence par `:' est un *mot-clé* (en Anglais : keyword). Techniquement, le caractère `:' ne fait pas partie du nom, mais indique le *paquetage* (en Anglais : package) dans lequel est contenu le symbole.

Pour tester si un symbole est un mot-clé, utiliser le prédicat *keywordp* :

\* (keywordp ':bonjour)

T \* (keywordp 'bonjour)

NIL \*

Les mots-clés sont particuliers pour plusieurs raisons. D'abord, la valeur d'un mot-clé est ce même mot-clé :

\* :ab

:AB \* :lskdfjlskdjflskdjf

:LSKDFJLSKDJFLSKDJF \*

Les mots-clés permettent de *nommer* des paramètres d'une fonction. C'est leur utilisation principale. Certaines fonctions ont besoin d'un grand nombre de paramètres, dont la majorité ne change pas d'une invocation à l'autre. Un exemple typique est une fonction de création d'une fenêtre dans un système de fenêtrage comme X11. Les paramètres d'une telle fonction déterminent la position et les dimensions de la fenêtre, la couleur du fond, la profondeur, la fenêtre mère, le masque d'événements, etc.

Dans un langage de programmation sans *paramètres nommés*, un appel de ce type de fonction est difficile à lire et comprendre, car il faut se souvenir de la signification de chaque paramètre, puis vérifier la position de chacun. Les paramètres nommés permettent de simplifier considérablement ce type d'appel. Exemple (de la bibliothèque CLX pour le système X11) :

(create-window :parent root                :x 10 :y 20                :width 200 :height 300)

Ici un mot-clé est utilisé pour nommer l'argument qui suit. L'ordre n'a pas d'importance. L'appel suivant est donc identique au précédent :

(create-window :y 20 :x 10                :width 200 :height 300                :parent root)

Autres utilisations des symboles

Les symboles ne sont pas utilisés uniquement pour la représentation des variables. À chaque symbole est associée une liste de propriétés (en Anglais : property list) que l'on peut utiliser pour fabriquer une base de données simple. La liste des propriétés d'un symbole est une liste dans laquelle un élément sur deux est un indicateur et un élément sur deux est une valeur. Les deux sont des objets Common Lisp arbitraires. La fonction get prend deux paramètres, un symbole et un indicateur et renvoie la valeur correspondant à l'indicateur, ou nil si l'indicateur n'existe pas pour le symbole. Avec setf, il est possible de changer ou créer la valeur associée à un indicateur. Exemples :

\* (get 'jane 'mother)

NIL \* (setf (get 'jane 'mother) 'anne)

ANNE \* (get 'jane 'mother)

ANNE \* (setf (get 'anne 'profession) 'carpenter)

CARPENTER \* (get (get 'jane 'mother) 'profession)

CARPENTER \*

**Caractères**

=caracteres.tex

;''

Chaînes de caractères

=chaines.tex

;''