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. »

Le Tao de la Récursion

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 :

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 :

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 :

  1. Un opérateur spécial (comme if),
  2. Une macro,
  3. 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 :

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 :

Il y a également des implémentations commerciales disponibles chez Franz, Xanalys, Digitool, Corman and Scieneer.

Voir aussi

Liens externes

Bibliographie

en ligne

en papier

See also: Common Lisp, ANSI, APL (langage), ASCII, Active server pages, Ada (langage), Assembleur (langage), Basic, Berkeley Software Distribution