AutoLISP : le guide complet — Chapitre 7 / 16

Les structures de contrôle

Un programme qui exécute toujours les mêmes instructions dans le même ordre est vite limité. Les structures de contrôle permettent à votre code de prendre des décisions et de répéter des actions. En AutoLISP, elles suivent la même syntaxe préfixée que tout le reste.

Les opérateurs de comparaison

Avant de pouvoir prendre des décisions, il faut pouvoir comparer des valeurs. AutoLISP propose les opérateurs suivants :

(= 5 5)      ; → T (égalité)
(/= 5 3)     ; → T (différent)
(< 3 5)      ; → T (inférieur)
(> 5 3)      ; → T (supérieur)
(<= 3 3)     ; → T (inférieur ou égal)
(>= 5 3)     ; → T (supérieur ou égal)

Ces fonctions retournent T (vrai) ou nil (faux).

Comparer des chaînes : attention à la casse !

La comparaison de chaînes avec = est sensible à la casse (case-sensitive) :

(= "bonjour" "bonjour")    ; → T
(= "bonjour" "Bonjour")    ; → nil (sensible à la casse !)
(= "BONJOUR" "bonjour")    ; → nil

C'est un piège fréquent, surtout quand on compare des saisies utilisateur ou des noms d'entités AutoCAD. Pour effectuer une comparaison insensible à la casse, convertissez les deux chaînes en majuscules (ou en minuscules) avec strcase avant de comparer :

(= (strcase "bonjour") (strcase "Bonjour"))  ; → T
(= (strcase "BONJOUR") (strcase "bonjour"))  ; → T

Bonne pratique : quand vous comparez une saisie utilisateur à une valeur attendue, pensez toujours à normaliser la casse avec strcase.

Comparer des nombres réels : attention aux arrondis !

Avec les nombres réels (à virgule flottante), la comparaison avec = peut donner des résultats surprenants à cause des erreurs d'arrondi inhérentes au calcul informatique :

(setq resultat (+ 0.1 0.2))
(= resultat 0.3)    ; → nil (!!)

Le résultat de (+ 0.1 0.2) n'est pas exactement 0.3 mais quelque chose comme 0.30000000000000004. C'est un problème classique qui touche tous les langages de programmation, pas seulement AutoLISP.

La solution est d'utiliser la fonction equal avec un argument de tolérance :

(equal (+ 0.1 0.2) 0.3 1e-10)  ; → T

Le troisième argument (1e-10, soit 0.0000000001) est la tolérance : si la différence entre les deux valeurs est inférieure à cette tolérance, equal considère qu'elles sont égales.

Règle d'or : ne comparez jamais deux nombres réels avec =. Utilisez toujours equal avec une tolérance appropriée. C'est particulièrement important en CAO où les coordonnées sont des réels et où les calculs géométriques accumulent les arrondis.

;; Comparer deux points (listes de réels) avec tolérance
(setq point1 '(10.0 20.0 0.0))
(setq point2 '(10.0000001 19.9999999 0.0))
(equal point1 point2 1e-6)  ; → T

equal fonctionne aussi avec des listes : elle compare les éléments un à un avec la tolérance donnée, ce qui est idéal pour comparer des points.

Les opérateurs logiques

Pour combiner des conditions :

(and T T)          ; → T (ET logique)
(and T nil)        ; → nil
(or nil T)         ; → T (OU logique)
(or nil nil)       ; → nil
(not nil)          ; → T (négation)
(not T)            ; → nil

and et or acceptent plusieurs arguments :

(and (> 5 3) (< 10 20) (= 1 1))  ; → T (toutes les conditions sont vraies)
(or (> 1 10) (< 5 3) (= 2 2))    ; → T (au moins une est vraie)

La condition if

La structure if est la plus simple des structures de décision :

(if condition
  expression-si-vrai
  expression-si-faux
)

Exemple :

(setq age 25)

(if (>= age 18)
  (alert "Vous êtes majeur.")
  (alert "Vous êtes mineur.")
)

Boite de dialogue affichant "Vous êtes majeur."

if sans else

L'expression « si faux » est optionnelle :

(if (> temperature 100)
  (alert "Attention : surchauffe !")
)

Si la condition est fausse et qu'il n'y a pas de branche « sinon », if retourne nil.

Exécuter plusieurs expressions : progn

if n'accepte qu'une seule expression par branche. Si vous avez besoin d'en exécuter plusieurs, regroupez-les avec progn :

(if (>= note 10)
  (progn
    (princ "\nBravo !")
    (princ "\nVous avez réussi l'examen.")
    (setq resultat "admis")
  )
  (progn
    (princ "\nDommage.")
    (princ "\nVous devrez repasser l'examen.")
    (setq resultat "refuse")
  )
)

progn exécute une séquence d'expressions et retourne la valeur de la dernière.

La condition multiple cond

Quand vous avez plus de deux cas à traiter, cond est plus lisible qu'une cascade de if imbriqués :

(cond
  (condition1 expression1)
  (condition2 expression2)
  (condition3 expression3)
  (T expressionParDefaut)
)

cond évalue les conditions de haut en bas et exécute la première dont la condition est vraie. La dernière ligne avec T comme condition sert de cas par défaut (puisque T est toujours vrai).

Exemple : classification d'un angle

(defun c:type-angle (/ angle)
  (setq angle (getreal "\nEntrez un angle en degrés : "))
  (cond
    ((= angle 0)   (alert "Angle nul"))
    ((< angle 90)  (alert "Angle aigu"))
    ((= angle 90)  (alert "Angle droit"))
    ((< angle 180) (alert "Angle obtus"))
    ((= angle 180) (alert "Angle plat"))
    ((< angle 360) (alert "Angle rentrant"))
    (T              (alert "Angle invalide ou complet"))
  )
  (princ)
)

Commande type-angle en action avec un angle de 45° classé comme "aigu"

Plusieurs expressions par cas

Chaque cas de cond peut contenir plusieurs expressions sans avoir besoin de progn :

(cond
  ((< note 10)
    (princ "\nRecalé.")
    (setq mention "insuffisant")
  )
  ((< note 12)
    (princ "\nPassable.")
    (setq mention "passable")
  )
  ((< note 14)
    (princ "\nAssez bien.")
    (setq mention "assez-bien")
  )
  (T
    (princ "\nBien ou très bien !")
    (setq mention "bien")
  )
)

La boucle while

while répète un bloc d'instructions tant qu'une condition est vraie :

(while condition
  expression1
  expression2
  ...
)

Exemple : compte à rebours

(setq compteur 10)

(while (> compteur 0)
  (princ (strcat "\n" (itoa compteur) "..."))
  (setq compteur (- compteur 1))
)

(princ "\nDécollage !")

Résultat dans la ligne de commande :

10...
9...
8...
...
1...
Décollage !

Exemple pratique : demander une valeur valide

(defun c:rayon-valide (/ rayon)
  (setq rayon 0)
  (while (<= rayon 0)
    (setq rayon (getreal "\nEntrez un rayon positif : "))
    (if (<= rayon 0)
      (princ "\nErreur : le rayon doit être supérieur à 0.")
    )
  )
  (alert (strcat "Rayon accepté : " (rtos rayon 2 2)))
  (princ)
)

Boucle de validation avec message d'erreur puis acceptation

La boucle repeat

repeat exécute un bloc un nombre fixe de fois :

(repeat nombre
  expression1
  expression2
  ...
)

Exemple :

(setq total 0)
(repeat 5
  (setq total (+ total 10))
)
total  ; → 50

Exemple : dessiner un polygone régulier

(defun c:polygone (/ nombre-cotes rayon centre angle increment point-courant)
  (setq nombre-cotes (getint "\nNombre de côtés : "))
  (setq rayon (getreal "\nRayon : "))
  (setq centre (getpoint "\nCentre : "))
  (setq increment (/ (* 2 pi) nombre-cotes))
  (setq angle 0)

  ;; Calculer le premier point
  (setq point-courant
    (list
      (+ (car centre) (* rayon (cos angle)))
      (+ (cadr centre) (* rayon (sin angle)))
    )
  )

  (repeat nombre-cotes
    (setq angle (+ angle increment))
    (setq point-suivant
      (list
        (+ (car centre) (* rayon (cos angle)))
        (+ (cadr centre) (* rayon (sin angle)))
      )
    )
    ;; Dessiner une ligne entre les deux points
    (command "LIGNE" point-courant point-suivant "")
    (setq point-courant point-suivant)
  )

  (princ)
)

Ce programme dessine un polygone régulier en calculant les sommets à l'aide de la trigonométrie. Ne vous inquiétez pas si vous ne comprenez pas toutes les fonctions utilisées (car, cadr, list, command) — nous les étudierons dans les chapitres suivants.

Résumé

Structure Rôle Syntaxe
if Condition simple (if test vrai faux)
progn Grouper des expressions (progn expr1 expr2 ...)
cond Conditions multiples (cond (test1 expr) (test2 expr) ...)
while Boucle conditionnelle (while test expr ...)
repeat Boucle fixe (repeat n expr ...)
and ET logique (and cond1 cond2)
or OU logique (or cond1 cond2)
not Négation (not condition)

Dans le prochain chapitre, nous plongeons au cœur de LISP : les listes, la structure de données la plus importante du langage.


Coup de pouce Besoin d'un développement AutoCAD (AutoLISP, ObjectARX, .NET, VBA) ? Contactez-moi pour un devis gratuit.