AutoLISP : le guide complet — Chapitre 13 / 16

Symboles et fonctions lambda

Ce chapitre aborde des concepts plus avancés qui font la richesse et l'élégance de LISP. Les symboles et les fonctions lambda vous permettront d'écrire du code plus expressif et plus puissant. Ces notions sont moins courantes dans les scripts AutoLISP du quotidien, mais les comprendre vous donnera une maîtrise bien plus profonde du langage.

Les symboles

En AutoLISP, un symbole est un nom qui référence une valeur. Quand vous écrivez (setq rayon 42), rayon est un symbole qui est lié à la valeur 42. C'est en quelque sorte l'étiquette sur une boite qui renferme la valeur 42.

Symbole rayon lié à la valeur 42

Quand AutoLISP rencontre un symbole, il l'évalue automatiquement — c'est-à-dire qu'il le remplace par sa valeur :

(setq rayon 42)
rayon          ; → 42 (le symbole est évalué)

Rappel : sur la ligne de commande d'AutoCAD, il faut préfixer le symbole par ! pour obtenir sa valeur (par exemple !rayon). Dans un programme .lsp ou dans la console de débogage de VS Code, ce préfixe n'est pas nécessaire.

Mais que se passe-t-il si vous voulez parler du symbole lui-même, sans l'évaluer ? C'est là qu'intervient quote.

La citation avec quote et '

La fonction quote (ou son raccourci ') empêche l'évaluation d'une expression :

(quote rayon)  ; → rayon (le symbole lui-même, pas sa valeur)
'rayon         ; → rayon (identique, syntaxe raccourcie)

Comparez :

(setq rayon 42)
rayon          ; → 42 (évaluation : retourne la valeur)
'rayon         ; → rayon (citation : retourne le symbole)

Différence entre évaluation et citation

C'est exactement ce mécanisme que vous avez utilisé pour créer des listes littérales :

'(1 2 3)       ; → (1 2 3) — la liste n'est pas évaluée comme un appel de fonction
(list 1 2 3)   ; → (1 2 3) — même résultat, mais via un appel de fonction

Sans l'apostrophe, (1 2 3) serait interprété comme un appel à la « fonction » 1, ce qui provoquerait une erreur.

La distinction entre quote et list est importante en pratique. Avec quote, tout le contenu est figé — aucune variable n'est évaluée. Avec list, les arguments sont évalués avant de construire la liste. Prenons l'exemple d'un filtre pour ssget :

;; Le nom du calque est connu à l'avance : quote suffit
(ssget "X" '((8 . "COTATION")))

;; Le nom du calque est dans une variable : il faut utiliser list
;; pour que la variable soit évaluée
(setq nom-calque (getstring "\nNom du calque : "))
(ssget "X" (list (cons 8 nom-calque)))

Avec '((8 . nom-calque)), AutoLISP chercherait littéralement un calque nommé "NOM-CALQUE" au lieu d'utiliser la valeur saisie par l'utilisateur.

eval : forcer l'évaluation

eval est l'inverse de quote. Il évalue une expression :

(setq expression '(+ 2 3))
expression          ; → (+ 2 3) — c'est une liste, pas un résultat
(eval expression)   ; → 5 — la liste est évaluée comme du code

C'est une capacité remarquable : en LISP, le code et les données ont la même structure (des listes). On peut donc construire du code dynamiquement et l'exécuter avec eval.

;; Construire une expression dynamiquement
(setq operateur '+)
(setq donnees '(10 20 30))
(eval (cons operateur donnees))  ; → 60 — évalue (+ 10 20 30)

set vs setq

Vous connaissez setq. La fonction set est sa version « sans quote » :

(setq x 10)      ; Affecte 10 au symbole x
(set 'x 10)      ; Identique : affecte 10 au symbole x

setq est un raccourci pour set quote — il cite automatiquement le premier argument. Mais set est utile quand le nom de la variable est lui-même dynamique :

(setq nom-variable 'ma-valeur)
(set nom-variable 42)
ma-valeur  ; → 42

En pratique, vous utiliserez presque toujours setq. Mais savoir que set existe vous aide à comprendre le fonctionnement interne du langage.

Les fonctions lambda

Une fonction lambda est une fonction anonyme — une fonction sans nom. Elle se définit avec le mot-clé lambda :

(lambda (x) (* x x))

Cette expression crée une fonction qui prend un argument x et retourne . Mais puisqu'elle n'a pas de nom, comment l'utiliser ?

Appeler directement une lambda

On peut l'appeler immédiatement en la plaçant en première position d'une expression :

((lambda (x) (* x x)) 5)  ; → 25

C'est équivalent à :

(defun carre (x) (* x x))
(carre 5)  ; → 25

Mais la version lambda n'a pas besoin de nom — elle est créée et utilisée en une seule fois.

Stocker une lambda dans une variable

On peut aussi affecter une lambda à une variable :

(setq carre (lambda (x) (* x x)))

Attention cependant : en AutoLISP, pour appeler une fonction stockée dans une variable, vous devrez utiliser apply (que nous verrons juste après).

Les fonctions d'ordre supérieur

Une fonction d'ordre supérieur est une fonction qui prend une autre fonction comme argument. C'est l'un des concepts les plus puissants de la programmation fonctionnelle, et AutoLISP en propose plusieurs.

mapcar : appliquer une fonction à chaque élément

mapcar applique une fonction à chaque élément d'une ou plusieurs listes et retourne une liste des résultats :

(mapcar '1+ '(1 2 3 4 5))
; → (2 3 4 5 6) — ajoute 1 à chaque élément

mapcar appliquant 1+

Avec une fonction lambda :

(mapcar '(lambda (x) (* x x)) '(1 2 3 4 5))
; → (1 4 9 16 25) — calcule le carré de chaque élément

mapcar avec plusieurs listes (les éléments sont pris en parallèle) :

(mapcar '+ '(1 2 3) '(10 20 30))
; → (11 22 33) — additionne les éléments deux à deux

apply : appliquer une fonction à une liste d'arguments

apply prend une fonction et une liste, et appelle la fonction avec les éléments de la liste comme arguments :

(apply '+ '(1 2 3 4 5))     ; → 15 — équivalent à (+ 1 2 3 4 5)
(apply 'max '(3 7 1 9 2))   ; → 9 — équivalent à (max 3 7 1 9 2)
(apply 'strcat '("Bon" "jour"))  ; → "Bonjour"

C'est très utile quand vous avez une liste de valeurs et que vous voulez les passer comme arguments individuels à une fonction.

vl-sort : trier une liste

vl-sort trie une liste en utilisant une fonction de comparaison :

(vl-sort '(3 1 4 1 5 9 2 6) '<)
; → (1 2 3 4 5 6 9) — tri croissant (les doublons sont supprimés)

(vl-sort '(3 1 4 1 5 9 2 6) '>)
; → (9 6 5 4 3 2 1) — tri décroissant (les doublons sont supprimés)

Attention : vl-sort peut supprimer les doublons de la liste. La documentation officielle indique : « Duplicate elements may be eliminated from the list ». Ce comportement n'est pas garanti — il dépend de l'implémentation interne — mais en pratique, les doublons sont systématiquement supprimés. Si vous avez besoin de conserver les doublons, triez plutôt les indices de la liste et reconstruisez-la ensuite.

Avec une lambda pour un tri personnalisé :

;; Trier des chaînes par longueur
(vl-sort '("chat" "hippopotame" "rat" "chien")
  '(lambda (a b) (< (strlen a) (strlen b)))
)
; → ("rat" "chat" "chien" "hippopotame")

vl-remove-if et vl-remove-if-not : filtrer

;; Garder uniquement les nombres pairs
(vl-remove-if-not
  '(lambda (x) (= (rem x 2) 0))
  '(1 2 3 4 5 6 7 8 9 10)
)
; → (2 4 6 8 10)

;; Supprimer les nombres négatifs
(vl-remove-if
  '(lambda (x) (< x 0))
  '(-3 5 -1 8 -7 2)
)
; → (5 8 2)

Exemple pratique : transformer des points

Imaginons que nous avons une liste de points 2D et que nous voulons les déplacer de 100 unités en X et 50 en Y :

(setq points '((0 0) (10 20) (30 40) (50 60)))

(setq points-deplaces
  (mapcar
    '(lambda (point)
      (list
        (+ (car point) 100)
        (+ (cadr point) 50)
      )
    )
    points
  )
)
; → ((100 50) (110 70) (130 90) (150 110))

Points originaux et déplacés

Comparons avec une approche impérative classique :

;; Version avec boucle
(setq points-deplaces '())
(foreach point points
  (setq points-deplaces
    (append points-deplaces
      (list
        (list
          (+ (car point) 100)
          (+ (cadr point) 50)
        )
      )
    )
  )
)

La version avec mapcar est plus concise et plus lisible une fois qu'on est familier avec ces concepts.

Résumé

Concept Syntaxe Description
Citation 'x ou (quote x) Empêche l'évaluation
Évaluation (eval expr) Force l'évaluation
Lambda (lambda (args) corps) Fonction anonyme
mapcar (mapcar 'fn lst) Applique une fonction à chaque élément
apply (apply 'fn lst) Appelle une fonction avec une liste d'arguments
vl-sort (vl-sort lst 'fn) Trie une liste
vl-remove-if (vl-remove-if 'fn lst) Filtre les éléments
vl-remove-if-not (vl-remove-if-not 'fn lst) Garde les éléments

Dans le prochain chapitre, nous verrons comment attacher des données personnalisées aux entités et lier des entités entre elles par leurs handles.


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