Common Lisp
| Image manquante Langage_progr.png image:Langage_progr.png |
| Cet article fait partie de la série Langages de programmation |
| Langages à objets |
| Ada 95 - C++ - C# Common Lisp Delphi - Eiffel - Java Nice - Oz - Python Simula - Smalltalk Visual Basic |
| Langages impératifs |
| APL - ASP - Assembleur BASIC - C - COBOL Forth - FORTRAN - Logo Pascal - Perl - PHP |
| Langages fonctionnels |
| Erlang - Haskell ML/OCaml - Oz Lisp/Common Lisp Scheme |
| Langages déclaratifs |
| Clips - Oz - Prolog |
| Langages concurrents |
| Ada 95 - Erlang Java - Oz |
| Langages balisés |
| HTML - SGML - XML Dialectes XML S-expressions |
| Voir aussi |
| Conception - Codage Tests - Optimisations |
« Common Lisp, la force d'une boule de glaise, est le patriarche bien vivant de la famille Lisp.
CL a été formé par des préoccupations techniques plus que par une quête de la clarté conceptuelle. Mais en dépit de son applicabilité pratique, CL offre le plaisir qui est tristement absent de la programmation de masse. »
| Sommaire |
Introduction
Common Lisp est un dialecte de Lisp standardisé par l'ANSI X3.226-1994. Développé pour standardiser les variantes divergentes de Lisp qui l'ont précédé, ce n'est pas une implémentation mais plutôt une spécification à laquelle la plupart des implémentations Lisp se conforment. Il est fréquement abrégé en CL.
Common Lisp est un langage de programmation à usage général, a contrario de dialectes de Lisp comme Emacs Lisp et AutoLisp, qui sont des langages d'extension embarqués dans des produits particuliers. Contrairement à de nombreux Lisp plus anciens, mais comme Scheme, Common Lisp utilise la portée lexicale par défaut pour les variables.
Common Lisp est un langage de programmation multi-paradigmes qui :
- Accepte des techniques de programmation impérative, fonctionnelle et orientée objet (CLOS).
- Est typé dynamiquement, mais avec des déclarations de type optionnelles qui peuvent améliorer l'efficacité et la sûreté,
- Dispose d'un système de gestion d'exceptions puissant, nommé Condition System (système de gestion de conditions),
- Est syntaxiquement extensible à travers des fonctionalités comme les macros et les macro de lecture.
Syntaxe
Common Lisp est un Lisp ; il utilise des s-expressions pour dénoter à la fois le code et les structures de données. Les appels de fonction et de macros sont écrits en tant que listes, avec le nom de la fonction en première place, comme dans ces exemples :
(+ 2 2) ; ajoute 2 et 2, renvoie 4 (setf e 2.7182817) ; assigne 2.7182817 à la variable e (defun carre (x) (* x x)) ; définit une fonction qui met un nombre au carré (carre 3) ; exécution de la fonction : retourne 9
Types de données
Common Lisp a une pléthore de types de données, plus qu'aucun autre langage.
Types scalaires
Nombres
Les types numériques incluent les entiers, les rationnels, les nombres à virgule flottante et les nombres complexes. Common Lisp utilise des Grands Nombres (en: bignums) pour représenter des valeurs numériques de taille et de précision arbitraires. Le type rationnel représente les fractions de façon exacte, une facilité absente de la plupart des langages. Common Lisp convertit automatiquement les valeurs numériques entre ces types de façon apropriée. Voici la tour numérique, c'est à dire la hiérarchie des types numériques de Common Lisp :
complex ratio fixnum
/ / /
number <--+-real--+-rational--+-integer--+-bignum
\ \
\ (1) signed-byte---unsigned-byte---bit
\
float--+-short-float
\-single-float
\-double-float
\-long-float
(1) integer et signed-byte sont des spécifications de types disjointes ; toutefois, les domaines sont identiques.
A titre d'exemple, l'expression
(+ (sqrt -2) (/ 6 4))
retourne
#C(3/2 1.4142135)
c'est à dire un nombre complexe dont la partie imaginaire est le flottant 1.4142135 et la partie réelle est le rationel 3/2.
Caractères
Le type Common Lisp caractère n'est pas limité aux caractères ASCII ; cela n'est pas surprenant car Lisp est plus ancien que l'ASCII. Certaines implémentations modernes supportent les caractères Unicode. [1]
Symboles
Le type symbole est commun aux langages Lisp, mais largement inconnu en-dehors. Un symbole est un objet nommé, unique. Les symboles en Lisp sont similaires aux identifieurs de variables dans d'autres langages, en ce qu'ils peuvent être utilisés comme variables pour stocker des valeurs ; toutefois, ils sont plus généraux et peuvent être utilisés pour eux-mêmes également. Normalement, lorsqu'un symbole est évalué, sa valeur en tant que variable est retournée. Il y a des exceptions, qui sont les mots-clefs (comme :foo ou :bar), et les valeurs booléennes qui sont représentées par les symboles réservés T et NIL.
Exemples :
foo ;; -> La variable FOO n'a pas de valeur. (function foo) ;; -> La fonction FOO n'est pas définie.
L'opérateur QUOTE protège les symboles de l'évaluation (lorsqu'on veut utiliser un symbole pour lui-même) :
(quote foo) ;; -> FOO 'foo ;; -> FOO
On peut demander si un symbole est lié à une valeur ou une fonction :
(boundp 'foo) ;; -> NIL (pas de valeur liée) (fboundp 'foo) ;; -> NIL (aucune fonction nommée FOO n'existe)
Association symbole-valeur :
(defparameter foo 77) ;; -> FOO foo ;; -> 77
Association symbole-fonction :
(defun foo (bar)
(1+ bar)) ;; -> FOO
Appel de la fonction FOO avec la valeur FOO (illustre le fait qu'un symbole dispose de slots séparés pour les valeurs et les fonctions) :
(foo foo) ;; -> 78
(boundp 'foo) ;; -> T (fboundp 'foo) ;; -> T (function foo) ;; -> #<CLOSURE FOO (BAR) (DECLARE (SYSTEM::IN-DEFUN FOO)) (BLOCK FOO (1+ BAR))>
Structures de données
Séquences
Les séquences sont un type abstrait représentant une collection ordonnée d'éléments. Les types concrets dérivés de séquence sont les listes et les vecteurs (et vecteurs de bits, chaînes).
Paires, Listes
Comme dans tout autre Lisp, les listes en Common Lisp sont composées de conses (pluriel), parfois appelés cellules cons ou paires. Un cons est une structure de données de deux éléments, appelés sont car et son cdr. Une liste est une chaîne de conses liés, où le cdr de chaque cons pointe sur l'élément suivant, et le dernier cdr pointe sur la valeur NIL. Les conses peuvent être facilement utilisés pour implémenter des arbres ou toute structures de données complexe ; bien que dans ce dernier cas il soit recommandé d'utiliser des structures ou des classes.
L'arbre (1 (2/7 3.14) A "foo") est représenté par la chaîne de CONS suivante : Image manquante
Lisp-liste.png
Il peut être construit de différentes façons, nous en citons deux :
(list 1 (list 2/7 3.14) 'a "foo") (cons 1 (cons (cons 2/7 (cons 3.14 NIL)) (cons 'a (cons "foo" NIL))))
Tableaux
Common Lisp supporte les tableaux de dimensions arbitraires, et peut aussi redimensionner dynamiquement les tableaux. Des tableaux multidimensionnels peuvent être utilisés pour les mathématiques des matrices. Seuls les tableaux à une dimension (nommés vecteurs) sont un sous-type de séquence. Les tableaux peuvent être spécialisés par le type des éléments qu'ils contiennent. En particulier, les vecteurs de bits et les vecteurs de caractères (chaînes) sont fournis en standard par le langage.
Tables de hachage
Les Tables de hachage stockent des associations entre objets. N'importe quel objet peut être utilisé comme clef ou valeur. Les tables de hachage, comme les tableaux, sont automatiquement redimensionnées si nécessaire.
Packages
Les Packages (paquets) sont des collections de symboles, utilisés principalement pour partitionner un programme en espaces de noms. Un paquet peut exporter certains symboles, les marquant comme une partie d'une interface publique. Les variables et méthodes dites privées des langages à objets classiques peuvent être obtenues en Lisp en les déclarant dans un espace de nom, sans les exporter.
Structures
Les Structures, similaires au structs du C et aux records (enregistrements) du Pascal, représentent des structures de données de complexité arbitraire, avec un nombre quelconque et tout type de champs (appelés slots). Les structures supportent une forme limitée d'héritage. Pour les besoins de la programmation orientée objet, on se reportera à CLOS.
Classes et Objets
Common Lisp a été le premier langage à objets standardisé (en 1995, par l'ANSI). La partie du langage traitant des objets se nomme CLOS pour Common Lisp Object System. CLOS est généralement considéré que le système à objets le plus général et le plus complet, en ce sens que les autres langages à objets peuvent être décrits dans les termes de CLOS, mais pas l'inverse. Les caractéristiques saillantes de CLOS sont les suivantes :
- c'est un système à classes (il existe en effet des systèmes à prototypes)
- les classes elles-mêmes sont des objets, ou instances de méta-classes (des classes de classes)
- il dispose d'un protocole à méta-objets (ou MOP pour Meta Object Protocol), lié à l'existence des méta-classes, et permettant de modifier la sémantique et le comportement du système,
- il permet l'héritage multiple entre classes,
- il offre la sélection multiple des méthodes, c’est-à-dire la sélection à l'exécution d'une méthode en fonction du type du tuple de ses arguments obligatoires (et non pas d'un receveur privilégié comme dans les langages à sélection simple, qui sont la très grande majorité)
- il permet la combinaison de méthodes, c’est-à-dire la définition de méthodes auxilaires s'exécutant avant et/ou après une méthode particulière.
CLOS permet également de définir des méta-classes et des classes, de changer la classe d'un objet, à l'exécution.
Le système de conditions de Common Lisp utilise CLOS pour définir les types des conditions pouvant survenir à l'exécution.
Fonctions et fermetures lexicales
Fonctions
En Common Lisp, les fonctions sont un type de donnée. Par exemple, il est possible d'écrire des fonctions qui prennent d'autres fonctions en argument, et retournent des fonctions (on les nomme fonctions d'ordre supérieur, ou de première classe). Cela rend possible d'écrire des opérateurs très généraux.
Exemple
Par exemple, la fonction sort (tri) prend une séquence et un opérateur de comparaison en argument. Elle peut être utilisée non seulement pour trier n'importe quel type de données, mais également pour trier des structures de données selon une clef.
(sort (list 5 2 6 3 1 4) #'>) ;; -> (6 5 4 3 2 1), en utilisant la fonction > comme opérateur de comparaison
(sort `((9 a) (3 b) (4 c))
(lambda (x y) (< (fist x) (first y)))) ;; -> ((3 b) (4 c) (9 a)), i.e. la liste triée sur le premier élément
On peut appliquer la fonction FOO définie plus haut à une séquence :
(mapcar #'foo (list 1 2 3 4 5)) ;; -> (2 3 4 5 6)
Espaces de noms
Common Lisp a un espace de nom respectivement pour les fonctions et pour les variables (à la différence de, par exemple, Scheme, qui est dit "Lisp-1"). Lisp-2 (ou plus) présente l'avantage qu'aucun nom de variable ne peut masquer un nom de fonction : on peut nommer une variable cons ou même if sans problème. Toutefois, pour faire référence à une fonction en tant que variable, on doit utiliser la fonction (function ...) ou la notation équivalente #' comme dans les exemples ci-dessus.
Outre les fonctions et les variables, il y a un espace de noms distinct pour les couples d'opérateurs block/return-from et tagbody/go.
Ajoutons pour finir que l'espace de nom des fonctions est en réalité partagé entre les fonctions proprement dites et les différentes sortes de macro.
Evaluation
Le modèle d'évaluation est simple : lorsque l'évaluateur rencontre une expression (F A1 A2 ... An), le symbole F peut représenter l'un de ces items :
- Un opérateur spécial (comme if),
- Une macro,
- Une fonction, c’est-à-dire le nom d'une fonction définie par (defun ...) ou une fonction anonyme, toujours dénotée par (lambda ...)
Si F est une fonction, les paramètres sont évalués successivement de droite à gauche et la fonction est invoquée avec les valeurs calculées des paramètres. Pour les opérateurs spéciaux ou les macros, cela dépend. Ces opérateurs tendent en effet à contrôler l'évaluation de leurs arguments. Par exemple, l'opérateur if n'évalue pas tous ses arguments, il doit évaluer sa condition et puis en fonction du résultat, une branche de l'aternative.
Capture lexicale
Une fermeture lexicale est une fonction dont les variables libres capturent les liaisons de l'environnement lexical dans lequel elles sont définie. Cela permet de construire des fonctions ayant un état interne (en C on utiliserait le mot-clef static pour obtenir l'état interne, mais la capture lexicale n'est pas possible). On peut construire des objets simples à partir de fermetures, par exemple une fabrique de compteurs :
(defun fabriquer-compteur () ; fabriquer-compteur renvoie une fonction qui incrémente et affiche sa valeur interne
(let ((valeur 0)) ; dans l'environnement de la fabrique, on crée la valeur du compteur
(lambda () ; le nouveau compteur lui-même
(incf valeur)))) ; ici, la référence à "valeur" capture sa définition dans la fabrique
Autres types
Les autres types de données de Common Lisp comprennent :
- les Pathnames (noms représentant des chemins) qui représentent fichiers et répertoires dans le système de fichier. L'outil de nommage de chemins en Common Lisp est plus général que la plupart des convention de nommage des systèmes d'exploitation, ce qui rend l'accès des programmes Lisp aux fichiers largement portable à travers différents systèmes.
- Les streams (flots) d'entrée et de sortie représentent des sources et des puits de données binaires et textuelles, comme le terminal ou des fichiers ouverts.
- Common Lisp possède son propre générateur de nombres pseudo-aléatoires. Les objets État aléatoire représentent des sources réutilisables de nombres pseudo-aléatoires, permettant à l'utilisateur d'initialiser le générateur ou de le forcer à rejouer une séquence.
- Les Conditions sont un type spécial utilisé pour représenter des erreurs, exceptions et autres évènements « intéressants » auxquels un programme doit pouvoir répondre. Common Lisp a l'un des système de gestion d'exceptions les plus complet ; il permet la reprise après erreur.
Common Lisp incorpore également une boîte à outils pour la programmation orientée objet, le Common Lisp Object System, ou CLOS. Il est donc possible d'ajouter une infinité de types.
Macros
Une macro en Lisp ressemble superficiellement à une fonction. Toutefois, plutôt que de représenter une fonction qui est évaluée, elle représente une transformation du texte du programme contenu dans l'appel à la macro.
Les macros permettent au programmeur Lisp de créer de nouvelles formes syntaxiques dans le langage. Par exemple, cette macro fournit la forme de boucle until (boucler... jusqu'à), qui est familière dans un langage comme Perl :
(defmacro until (test &rest body)
`(do ()
(,test)
,@body))
Exemple :
(until (= (random 10) 0) (write-line "Hello"))
Les macros ne sont pas évaluées à l'exécution comme les fonctions. On parle de macro-expansion, qui a lieu au moment de la compilation du code source. On peut toutefois les considérer comme des fonctions qui acceptent et retournent des arbres de syntaxe abstraits (les s-expressions), mais contrôlent l'évaluation de leurs paramètres. Comme les fonctions, elles peuvent utiliser l'ensemble du langage Common Lisp (et bibliothèques tierces) pour effectuer leur travail de transformation, contrairement aux macros du langage C qui ne permettent que des substitutions de chaînes de caractères au niveau du source, sans accès à l'ensemble du langage C lui-même.
Capture de variable
Les macros Common Lisp sont capables de capture de variable, une situation où des symboles situés dans le corps de la macro-expansion coincident avec des symboles du contexte apelant. Pour cette raison elles sont parfois apelées macro « non hygiéniques », par comparaison avec le système de « macro hygiéniques » de Scheme, qui garantit la séparation entre ces ensembles de symboles.
La capture de variable est parfois un effet désiré ; lorsque ce n'est pas le cas, elle doit être évité par l'emploi de gensyms, ou symboles dont l'unicité est garantie.
Implémentations
Common Lisp est défini par une spécification (comme Ada et C) plutôt que par une seule implémentation (comme Perl ou Python). Il y a de nombreuses implémentations, et le standard décrit les points sur lesquels elles peuvent être divergentes pour de bonnes raisons.
De plus, les implémentations sont généralement fournies avec différents ensembles de « paquets » de bibliothèques, qui fournissent des fonctionnalités non couvertes par le standard. Certaines de ces fonctionnalités ont été introduites par la suite dans le standard, comme CLOS et la forme LOOP ; d'autres restent propres à ces implémentations. De nombreuses commodités pour le programmeur moderne -- comme l'accès aux réseaux TCP/IP -- restent hors du standard, mais sont fournies par les différentes implémentations avec parfois des différences mineures. Un processus nommé CLRFI (Common Lisp Request For Improvement), similaire aux SRFI de Scheme, a pour objectif de faire converger les fonctionalités utiles laissées hors du standard ANSI de 1995.
Selon une erreur répandue, les implémentations de Common Lisp sont toutes des interpréteurs. En fait, la compilation fait partie de la spécification du langage. La plupart des implémentations de Common Lisp compilent les fonctions vers du code machine. D'autres compilent vers du code objet, ce qui réduit la vitesse mais améliore la portabilité.
Certaines implémentations en environnement UNIX, comme CLISP, peuvent être utilisées comme des interpréteurs de scripts (ou même comme shell).
Liste des implémentations
Les implémentations librement redistribuables incluent :
- CMU Common Lisp (CMUCL), de Carnegie Mellon University. CMUCL est peut-être le CL le plus largement utilisé parmi les logiciels libres. Il est disponible sur Linux et BSD pour Intel x86; Linux pour Alpha; et Solaris, IRIX, et HP-UX sur leurs plate-formes natives.
- GNU CLISP, une implémentation compilant du code-objet. Il est portable et tourne sur de nombreux UNIX et systèmes de type UNIX, de même que sur Microsoft Windows et plusieurs autres systèmes.
- Steel Bank Common Lisp (SBCL), une branche de CMUCL. « En gros, SBCL se distingue de CMU CL par une plus grande insistance sur la maintenabilité.. ». Sur Linux 2.6, SBCL supporte les threads natifs. [2] SBCL tourne sur les mêmes plate-formes que CMUCL, moins HP/UX, et plus Linux pour PowerPC, SPARC, et MIPS.
- GNU Common Lisp (GCL), le compilateur Lisp du projet GNU. Pas encore complètement conforme au standard ANSI, GCL est cependant l'implémentation de choix pour plusieurs gros projets, incluant les outils mathématiques Maxima et ACL2. GCL tourne sur GNU/Linux sur onze différentes architectures, et également sous Windows, Solaris, et FreeBSD.
- Embeddable Common Lisp (ECLS), conçu pour être embarqué dans des applications écrites en C.
- OpenMCL, une branche Open Source de Macintosh Common Lisp. Comme son nom l'indique, OpenMCL est natif sur le Macintosh ; il s'exécute sur Mac OS X, Darwin, et Linux pour PowerPC.
- Armed Bear Common Lisp, est une implémentation, en développement, d'un compilateur CommonLisp → machine virtuelle Java. ABCL est fourni avec un éditeur à tout faire, J, écrit en Common Lisp.
Il y a également des implémentations commerciales disponibles chez Franz, Xanalys, Digitool, Corman and Scieneer.
Voir aussi
Liens externes
- L'aggrégateur Planet Lisp
- La Common Lisp HyperSpec, une version hypertextuelle du standard.
- Le CLiki, un Wiki Common Lisp.
- The Association of Lisp Users.
- The Common Lisp Cookbook, une collection de méthodes de programmation utiles.
- Le site de Paul Graham, un développeur et avocat de l'utilisation de Common Lisp ; son site contient de nombreux essais sur Lisp et la programmation en général.
Bibliographie
en ligne
- Successful Lisp, un livre sur la programmation en Common Lisp.
- Traité de Programmation en Common Lisp, un autre livre, en français, sur la programmation en Common Lisp.
- On Lisp, un livre de Paul Graham sur des techniques avancées (en particulier l'art des macros).
- Common Lisp: A Gentle Introduction to Symbolic Computation Un livre très facile d'accès qui présente progressivement les concepts du langage Lisp.
- Practical Common Lisp, un livre publié en 2005 sur la constructions d'applications en Common Lisp. Excellente introduction à de nombreuses fonctionalités avancées du langage, d'un point de vue agréablement concret.
en papier
- ANSI Common Lisp, de Paul Graham chez Prentice Hall (1995)
- Common Lisp, de Wade Hennessey chez Mcgraw-Hill College (1989)
- Common Lisp: A Gentle Introduction to Symbolic Computation, de David Touretzky chez Benjamin-Cummings Pub Co (1989)
- Common LISP : The Language, de Guy Steele chez Digital Press (1991)
- Object-Oriented Common LISP, de Stephen Slade chez Prentice Hall (1997)
- Object-Oriented Programming : The CLOS Perspective, de Adreas Paepcke chez MIT Press (1994)
- Object-Oriented Programming in Common Lisp: A Programmer's Guide to CLOS, de Sonya Keene chez Addison-Wesley (1989)
- Paradigms of Artificial Intelligence Programming : Case Studies in Common Lisp, de Peter Norvig chez Morgan Kaufmann (1991)
- Practical Common Lisp, de Peter Seibel chez Apress (2005)
- Successful Lisp: How to Understand and Use Common Lisp, de David Lamkins chez BookFix (2004)
- The ANSI Common Lisp Reference Book, de David Margolies chez Apress (2005)
- The Art of the Metaobject Protocol, de Gregor Kiczales chez MIT Press (1991)
