cond

On a souvent besoin d'imbriquer plusieurs conditionnelles dans une chaîne de tests. Avec if, ce type de chaîne risque de provoquer une indentation importante. Exemple :

(if (< x 12)
    (...)
    (if (< x 28)
        (...)
        (if (< x 55)
            (...)
            (...))))

Pour gérer cette situation fréquente, Common Lisp propose une conditionnelle très générale : cond. Avec cond, il est possible de donner une suite de clauses, chacune contenant une condition et une suite (progn implicite) d'expressions à évaluer si la condition vaut vrai. Les clauses sont indentées de la même manière, ce qui évite le gaspillage d'indentation. L'exemple ci-dessus se transforme alors en :

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

La clause commençant par t n'est pas obligatoire. Elle sert de condition sinon finale et les expressions associées seront évaluées si aucune autre condition ne vaut vrai. Alors que n'importe quelle expression dont la valeur est vrai pourrait être utilisée, il est conventionnel d'utiliser t pour signaler la dernière phrase. Voici quelques exemples de McCLIM :

(cond ((= max r) (/ (- g b) diff))
      ((= max g) (+ 2 (/ (- b r) diff)))
      (t (+ 4 (/ (- r g) diff))))

(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)))

Le dernier exemple montre une utilisation de cond pour laquelle la seule raison de ne pas avoir utilisé un if est le progn implicite de la première clause. Avec if on aurait :

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

Le cond offre aussi la possibilité de mettre des clauses ne contenant que la condition. Si la condition d'une telle clause vaut vrai, c'est cette valeur qui sera la valeur de l'expression cond entière. Dans l'exemple suivant (de McCLIM), l'expression (gethash name *command-tables*) renvoie nil si le tableau de hachage *command-tables* ne contient pas d'objet avec une clé égale à name. Sinon, elle renvoie l'objet :

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

Irene DURAND
2011-10-14