Synthèse de la discussion : Arguments et Variables en AutoLISP sur CADxp
Ce document propose une synthèse d’une discussion technique issue du forum CADxp, une ressource francophone de référence pour les utilisateurs de logiciels de CAO. Le thème central, soulevé par un utilisateur débutant, est la distinction fondamentale entre les arguments et les variables en AutoLISP. Ce sujet, bien que basique en apparence, est crucial pour acquérir de bonnes pratiques de programmation et éviter des erreurs courantes. Cette synthèse retrace chronologiquement les échanges pour en extraire les explications clés, les exemples didactiques et les concepts avancés qui en ont découlé. L’objectif est de fournir un aperçu structuré et pédagogique de ce fil de discussion enrichissant.
Pour une lecture complète des échanges, la discussion originale est accessible ici : Arguments et Variables - Débuter en LISP - CadXP
1. Le Point de Départ : La Question d’un Débutant
La discussion a été initiée le 5 septembre 2010 par l’utilisateur Fraid @Fraiddd . Sa question, portant sur la syntaxe de la déclaration d’une fonction (defun nom (argument / variable)), révèle une confusion commune chez les programmeurs LISP novices concernant le rôle et la nature des différents symboles déclarés dans l’en-tête d’une fonction.
La question centrale posée par Fraid peut être retranscrite comme suit :
Bonjour, defun truc (argument / variable) Quelque temps que je cherche quelque explication sur ce qu’il se passe entre les parentheses se trouvant apres le defun.
l’argument est il une variable déclaré dans une autre fonction? qui n’est pas remis a nil ?
Quand est on obligé de déclarer les variables dés le depart ?
(j’ai un certain nombre de lisp qui fonctionne tres bien avec les parentheses vides)
Ses interrogations principales étaient donc :
- La nature d’un « argument » : Est-ce une variable externe transmise à la fonction ?
- La nécessité de déclarer les variables dès le début d’une fonction.
- La raison du fonctionnement des fonctions avec des parenthèses vides
().
Cette question fondamentale a immédiatement suscité l’intérêt de la communauté et a reçu une réponse très détaillée de la part du modérateur expert (gile).
2. Le Cœur de la Résolution : L’Explication Détaillée de (gile)
L’utilisateur (gile) a fourni une réponse complète qui a constitué le point de résolution principal du problème initial. Son explication a été structurée en plusieurs parties pour aborder chaque aspect de la question de manière didactique, en commençant par les bases avant d’aborder des concepts plus complexes.
2.1. Les Variables : Distinction entre Locales et Globales
(gile) commence par définir une variable comme un symbole auquel est liée une valeur, typiquement via la fonction (setq). Il établit ensuite la distinction cruciale entre les variables globales et locales.
- Variables Globales : Elles sont définies en dehors de toute fonction (par exemple, directement dans la ligne de commande). Elles conservent leur valeur tout au long de la session de dessin et sont accessibles de partout. Ce comportement peut être dangereux, car une fonction peut modifier une variable globale de manière inattendue, créant des « effets de bord ». Par prudence, il est recommandé d’utiliser une convention de nommage spécifique, comme
*variableGlobale*, pour les identifier clairement. - Variables Locales : Elles sont déclarées après le symbole
/dans la définition de la fonction, par exemple(defun foo (/ a)). Cette déclaration les « isole » : leur existence et leur valeur sont confinées à l’intérieur de la fonction. Cela empêche les conflits avec des variables de même nom existant à l’extérieur et garantit que la fonction ne modifie pas l’environnement global de manière imprévue.
Il conclut cette partie par une recommandation essentielle : « En résumé, à part quelques rares exceptions, il est toujours préférable de déclarer localement les variables utilisées dans une fonction. »
2.2. Les Arguments : Le Canal de Communication des Fonctions
La seconde partie de l’explication définit un argument comme une donnée passée à une fonction pour permettre son exécution. Il différencie ensuite les deux principaux types de fonctions qu’un utilisateur peut définir en AutoLISP.
| Type de Fonction | Description | Gestion des Arguments |
|---|---|---|
| Commande LISP | Préfixée par c:, elle peut être appelée directement depuis la ligne de commande d’AutoCAD®. |
Ne peut pas utiliser d’arguments lors de son appel depuis la ligne de commande. |
| Sous-routine LISP | Utilisable comme une fonction prédéfinie, elle est appelée par d’autres fonctions LISP. | Peut (et doit souvent) utiliser un nombre défini d’arguments pour fonctionner. |
Pour illustrer le concept, (gile) propose un exemple simple : une fonction sqr qui calcule le carré d’un nombre.
(defun sqr (num) ; num est l'argument de la fonction sqr
(* num num) ; l'évaluation de cette expression est le retour de la fonction sqr
)
Ici, num est l’argument. Lorsqu’on appelle (sqr 5), num reçoit la valeur 5 à l’intérieur de la fonction, qui retourne alors 25. Comme le souligne (gile), les arguments se comportent comme des variables locales : toute modification de leur valeur à l’intérieur de la fonction n’affecte pas la variable d’origine qui a pu être utilisée pour l’appel.
2.3. Application Pratique : La Factorisation du Code
(gile) introduit ensuite le concept de « factorisation ». Il s’agit du processus consistant à extraire un bloc de code récurrent d’une fonction principale pour en faire une sous-routine générique et réutilisable.
Il illustre ce principe avec la transformation de la commande c:ssTo0, qui déplace une sélection d’entités vers le calque « 0 ». Le code qui modifie le calque d’une seule entité est extrait pour créer une nouvelle fonction plus polyvalente : (defun ChangeLayer (ent lay / elst)). Dans cette sous-routine, le nom de l’entité (ent) et le nom du calque cible (lay) sont passés comme arguments, ce qui permet de l’utiliser pour déplacer n’importe quelle entité vers n’importe quel calque, et pas seulement vers le calque « 0 ».
Cette clarification des bases a ouvert la voie à une discussion sur des concepts AutoLISP plus avancés.
3. Au-delà des Bases : Fonctions d’Ordre Supérieur et Fonctions Anonymes
Une fois les fondamentaux établis, (gile) a approfondi le sujet en introduisant des concepts de programmation fonctionnelle, notamment la possibilité de passer une fonction en argument à une autre fonction.
apply: Applique une fonction à une liste d’arguments. Par exemple,(apply '+ '(1 2 3))est équivalent à(+ 1 2 3)et retourne6. Son utilité réside dans le traitement de listes dont le contenu n’est pas connu à l’avance.mapcar: Applique une fonction à chaque élément d’une ou plusieurs listes et retourne une nouvelle liste contenant les résultats. Par exemple,(mapcar '1+ '(1 2 3))applique la fonction « incrémenter de 1 » à chaque nombre et retourne(2 3 4).
Il a également introduit la notion de fonction anonyme avec lambda. Une fonction lambda permet de définir une fonction « à la volée », sans avoir à la nommer avec defun. C’est particulièrement utile en conjonction avec des fonctions comme mapcar. L’exemple (mapcar '(lambda (num) (* num num)) '(1 2 3)) montre comment on peut appliquer une fonction « carré » à chaque élément d’une liste sans avoir à définir préalablement une fonction sqr.
Ces explications ont naturellement conduit à une discussion plus fine sur la manière dont les variables se comportent dans des contextes d’appels de fonctions imbriquées.
4. La Portée des Variables et les « Effets de Bord »
La discussion a ensuite évolué vers l’analyse de la portée des variables (scope) et des « effets de bord » (side effects), des comportements qui peuvent causer des erreurs difficiles à déboguer s’ils ne sont pas maîtrisés.
Dans un message du 16 octobre 2010, (gile) a fourni une démonstration claire avec deux fonctions, main et sub. L’exemple montre qu’une variable c, déclarée localement dans main, voit sa valeur modifiée par un appel à sub (où c n’est pas déclarée localement). Cette modification persiste au retour dans main. C’est l’illustration parfaite d’un « effet de bord » : une fonction modifie un état qui n’est pas local à son propre environnement.
Ce sujet a suscité un débat plus approfondi en avril 2011 entre mattoti, Bred et (gile). La confusion des utilisateurs portait sur le concept de portée d’une variable locale d’une fonction A vers une fonction B appelée par A, et en particulier sur la notion de valeur « temporairement écrasée ». Après plusieurs échanges visant à clarifier ce point, (gile) a fourni une synthèse finale qui sert de règle générale en AutoLISP :
« De la même manière qu’une variable globale dans le document garde sa valeur dans une fonction si elle n’est pas déclarée ou est temporairement « écrasée » si elle est déclarée, une variable déclarée dans une fonction conserve sa valeur dans une sous-fonction appelée par celle-ci si elle n’est pas déclarée dans la sous fonction ou est temporairement écrasée si elle est déclarée dans la sous fonction. »
5. Synthèse et Conclusion
La question initiale, simple et directe, posée par un débutant, a servi de catalyseur à une exploration approfondie et remarquablement pédagogique des concepts d’AutoLISP. La discussion a couvert non seulement les fondamentaux (distinction entre variables locales/globales et arguments), mais aussi des techniques de programmation avancées comme la factorisation du code, l’utilisation de fonctions d’ordre supérieur (mapcar, apply), les fonctions anonymes (lambda) et les subtilités de la portée des variables.
La clarté et la progressivité des explications, en particulier celles fournies par (gile), ont non seulement permis de résoudre le problème initial de l’auteur, mais ont également transformé ce fil de discussion en une ressource pédagogique de grande valeur. Ce fil sert de modèle pour l’apprentissage collaboratif, où une simple question de débutant, grâce à des conseils d’experts, se transforme en une véritable masterclass sur les principes fondamentaux de la programmation.
Pour une lecture complète et détaillée des échanges, les lecteurs peuvent se référer à la source de cette discussion via le lien fourni en début d’article.