Cours 3

Ce cours s'adresse aux étudiants en informatique souhaitant approfondir leur connaissance du langage Common Lisp. Il couvre les notions fondamentales liées aux symboles, aux constantes, à la documentation, aux opérateurs spéciaux, ainsi qu'aux instructions conditionnelles et à la gestion des variables locales.

D'après le document Cours 3

Cet article a été rédigé automatiquement à partir du document source, puis vérifié avant publication.

Cours 3

Document source

Cours 3

Programming, Math, etc. · DOCX · 12 pages

Consulter le document original →

Ce cours s'adresse aux étudiants en informatique souhaitant approfondir leur connaissance du langage Common Lisp. Il couvre les notions fondamentales liées aux symboles, aux constantes, à la documentation, aux opérateurs spéciaux, ainsi qu'aux instructions conditionnelles et à la gestion des variables locales.

Symboles en Common Lisp

Un symbole en Common Lisp est l'équivalent d'un identificateur dans d'autres langages, mais avec une syntaxe plus souple. Il peut contenir une grande variété de caractères, y compris des lettres, chiffres, et certains symboles comme +, -, *, /, !, <, >, =, ^, à condition que la chaîne ne soit pas interprétable comme un nombre. Certains caractères spéciaux comme les parenthèses ou espaces peuvent être inclus s'ils sont précédés d'un anti-slash ou si le symbole est entouré de barres verticales |.

Lors de l'évaluation, un symbole est traité comme une variable dont la valeur est recherchée dans l'environnement courant. Common Lisp possède de nombreuses variables prédéfinies, souvent entourées d'astérisques par convention, comme *read-base*.

Pour définir une variable globale, on utilise les macros defparameter ou defvar. La différence est que defparameter réinitialise la variable même si elle existe déjà, tandis que defvar ne l'initialise que si elle n'existe pas encore. Voici quelques exemples :

(defparameter a>b "peut-etre")
a>b  ; => "peut-etre"

(defparameter 2^4 16)
|2^4|  ; => 16

(defparameter \( \( bonjour 4)
|( BONJOUR|  ; => 4

Notez que les lettres minuscules sont automatiquement converties en majuscules, sauf si elles sont précédées d'un anti-slash ou entourées de barres verticales. Les symboles nil et t sont prédéfinis et spéciaux : nil représente aussi la valeur booléenne faux, et t la valeur vrai.

Un symbole peut aussi représenter le nom d'une fonction, d'une macro ou d'un opérateur spécial, ou être utilisé comme un objet normal.

Effets de bord et affectation

En Common Lisp, presque toutes les expressions ont une valeur, mais certaines provoquent aussi des effets de bord, c’est-à-dire des modifications de l'environnement. L'affectation est l'effet de bord le plus courant, réalisée avec la macro setf. Cette macro prend deux arguments : une place (souvent une variable) non évaluée, et une expression évaluée dont la valeur est affectée à la place.

(setf *print-level* 10)  ; Affecte la valeur 10 à la variable *print-level*
*print-level*             ; => 10

La variable *print-level* contrôle le nombre de niveaux de listes affichés par print. Sa valeur par défaut nil signifie aucune limite.

La définition de fonctions est aussi un effet de bord, réalisée avec la macro defun. Elle prend comme arguments le nom de la fonction, une liste de paramètres, puis une séquence d'expressions formant le corps de la fonction. Exemple :

(defun pythagora (x y)
  (sqrt (+ (* x x) (* y y))))

(pythagora 3 4)  ; => 5.0

La définition retourne le nom de la fonction (un symbole). L'indentation du corps est obligatoire et facilitée par les éditeurs comme Emacs.

L'opérateur spécial QUOTE

L'opérateur spécial quote empêche l'évaluation de son argument, retournant l'argument tel quel. Il prend un seul paramètre :

(quote *read-base*)  ; => *READ-BASE*
(quote (setf *read-base* 8))  ; => (SETF *READ-BASE* 8)

On peut aussi utiliser la forme abrégée avec l'apostrophe :

'(this is a test)  ; => (THIS IS A TEST)
'*read-base*        ; => *READ-BASE*

La fonction eval permet d'évaluer une expression construite dynamiquement, mais son usage est rare et souvent déconseillé au profit des macros.

Instructions conditionnelles

Common Lisp propose plusieurs formes conditionnelles adaptées à différentes situations : if, cond, case, ainsi que when et unless (non détaillés ici).

La forme cond

La forme cond permet d'enchaîner plusieurs tests sans imbriquer plusieurs if, ce qui améliore la lisibilité grâce à une indentation uniforme. Chaque clause contient une condition et une ou plusieurs expressions à évaluer si la condition est vraie. La dernière clause peut être une clause t servant de cas par défaut.

(cond ((< x 12) (...))
      ((< x 28) (...))
      ((< x 55) (...))
      (t (...)))

Exemple d'utilisation avec plusieurs expressions dans une clause :

(cond ((slot-value pane 'armed)
       (draw-rectangle* pane 0 0 w h :ink (gadget-highlighted-color pane) :filled t)
       (draw-edges-lines* pane (- w 2) (- h 2) 1 1))
      (t
       (draw-rectangle* pane 0 0 w h :ink (gadget-normal-color pane) :filled t)))

Dans ce cas, cond évite d'utiliser progn explicitement.

Une clause peut aussi ne contenir qu'une condition, auquel cas la valeur de cette condition sera la valeur de l'expression cond si elle est vraie :

(cond ((command-table-p name) name)
      ((gethash name *command-tables*))
      (errorp (error 'command-table-not-found))
      (t nil))

La forme if

La forme if est la plus simple. Elle prend une condition, une expression à évaluer si la condition est vraie, et une expression optionnelle sinon. Si la partie sinon est omise, sa valeur est nil par défaut, mais cela est considéré comme un mauvais style.

(if (> *read-base* 7)
    (progn (print "hello") (+ *read-base* 22))
    11)
; Affiche "hello" puis retourne 32

(if (< *read-base* 7)
    (progn (print "hello") (+ *read-base* 22))
    11)
; Retourne 11 sans afficher "hello"

En Common Lisp, toute valeur autre que nil est considérée comme vraie, y compris 0.

La forme case

La macro case est similaire à cond, mais elle compare la valeur d'une expression à une liste d'objets dans chaque clause. Si la valeur correspond à un des objets, les expressions de la clause sont évaluées. La comparaison utilise le prédicat eql.

Exemple :

(case after
  (:start (push new-item menu))
  ((:end nil) (setf menu (nconc menu (list new-item))))
  (:sort (setf menu (sort (cons new-item menu)
                          #'string-lessp
                          :key #'command-menu-item-name)))
  (t (push new-item
           (cdr (member after menu
                        :key #'command-menu-item-name
                        :test #'string-equal)))))

La clause t sert de cas par défaut. Chaque clause commence par un objet ou une liste d'objets à comparer. Pour éviter toute ambiguïté, les clauses (sauf la dernière) ne peuvent pas commencer par t ou otherwise seuls, mais doivent les entourer d'une liste, par exemple (t).

Attention, si la valeur est une chaîne de caractères ou une liste, case peut ne pas fonctionner comme attendu car eql ne compare pas ces types par contenu. Exemple :

(defparameter abc "abc")

(case abc
  ("fga" 'no)
  ("abc" 'yes)
  (t 'none))
; => NONE (car "abc" ≠ "abc" selon eql)

Il existe aussi ecase qui est similaire à case mais génère une erreur si aucune clause ne correspond et qu'il n'y a pas de clause t.

case est souvent plus performant que cond car les étiquettes sont des constantes connues à la compilation, permettant des optimisations comme le hachage parfait.

Glossaire des termes clés

  • Symbole : Identificateur en Common Lisp pouvant contenir une large gamme de caractères, représentant souvent une variable, une fonction, ou un opérateur.
  • defparameter : Macro pour définir une variable globale, réinitialisant sa valeur même si elle existe déjà.
  • defvar : Macro pour définir une variable globale, initialisant seulement si elle n'existe pas.
  • setf : Macro d'affectation, modifiant la valeur d'une place (variable ou autre).
  • defun : Macro pour définir une fonction avec un nom, une liste de paramètres et un corps d'expressions.
  • quote : Opérateur spécial empêchant l'évaluation de son argument.
  • eval : Fonction évaluant une expression construite dynamiquement.
  • if : Instruction conditionnelle simple avec condition, alors, et sinon.
  • cond : Instruction conditionnelle multiple avec une liste de clauses condition-expressions.
  • case : Instruction conditionnelle basée sur la correspondance d'une valeur avec des constantes.
  • nil : Symbole représentant la valeur booléenne faux et la liste vide.
  • t : Symbole représentant la valeur booléenne vrai.

Points clés à retenir

  • Les symboles en Common Lisp sont des identificateurs flexibles pouvant contenir divers caractères.
  • defparameter et defvar servent à définir des variables globales avec des comportements différents.
  • setf est la macro standard pour l'affectation en Common Lisp.
  • La définition de fonctions s'effectue avec defun, qui retourne le nom de la fonction.
  • quote empêche l'évaluation d'une expression, utile pour manipuler des données symboliques.
  • if, cond et case sont les principales formes conditionnelles, choisies selon la complexité et la nature des tests.
  • cond facilite l'écriture de chaînes de tests multiples sans imbriquer plusieurs if.
  • case compare une valeur à des constantes et peut être plus performant que cond, mais utilise eql pour la comparaison.

Partager

Commentaires

Aucun commentaire pour le moment. Posez la première question.

Les commentaires sont relus avant publication. Votre e-mail n'est jamais affiché.

← Toutes les révisions