Common Lisp - Définition

Source: Wikipédia sous licence CC-BY-SA 3.0.
La liste des auteurs de cet article est disponible ici.

Introduction

Common Lisp est un langage fonctionnel impur de la famille Lisp.

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 une spécification à laquelle les implémentations Lisp essayent de se conformer. Il est fréquemment 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 fonctionnalités comme les macros et les macros de lecture.

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 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. Common Lisp convertit automatiquement les valeurs numériques entre ces types de façon approprié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 rationnel 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, relatif à un espace de noms. Les symboles littéraux en Lisp sont utilisés comme identificateurs de variables ; toutefois, ils sont plus généraux et peuvent être utilisés pour eux-mêmes également, comme des objets. Lorsqu'un symbole est évalué, sa valeur en tant que variable est retournée.

L'existence des symboles en tant que type de données facilite l'écriture des macros (qui effectuent des transformation de code au moment de la compilation) et l'implémentation de systèmes de calcul symbolique. De fait, Lisp a été le premier langage d'implémentation de systèmes de calcul formel. Des systèmes de calcul formel très complets ont été écrit en Lisp (Maxima et Axiom, et pour les transformations symboliques, ils soutiennent généralement la comparaison avec les populaires Mathematica et Maple).

Il existe des cas particuliers :

  • les valeurs booléennes sont représentées par les symboles réservés T et NIL.
  • les mots-clefs (de la forme :foo ou :bar) sont des symboles définis dans l'espace de nom KEYWORD. L'évalutation d'un tel symbole retourne le symbole lui-même.

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)         ;; -> #      

Recursivité :

      (defun factoriel (n)      	(if (= n 0)      		1      		(* n (factoriel(- n 1)))))      

Autre :

      (first '(7 3 10)) ;;-> 7      (rest '(20 2 45)) ;;-> (2 45)      (endp '()) ;;-> T      (endp '(5)) ;;-> NIL liste non vide      

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 (y compris les vecteurs de bits et chaînes de caractères). De nombreuses fonctions sont disponibles pour les séquences. Il n'est toutefois pas possible au programmeur de définir ses propres sous-types de séquences. Une extension du standard permettant la définition de nouvelles séquences est en cours de proposition (circa 2007).

Paires, Listes

Les listes de Common Lisp ne sont pas un type de donnée mais sont composées à partir de conses (pluriel), parfois appelés cellules cons ou paires. Un cons est une structure à deux éléments de type T, accessibles par les fonctions car et cdr (ou encore first et rest). Une liste est donc 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 : 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))))      

Les listes ou structures cycliques faites à partir de paires n'ont pas de représentation littérale, mais elles peuvent être imprimées :

       (setf *print-circle* t)             ; active l'affichage des structures circulaires (évite le bouclage infini)       (let ((list (copy-list '(a b c))))           (setf (cdr (last list)) list))   ; =>   #1=(A B C . #1#)      

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.

Exemple de creation de vecteur :

      (setq v (make-array 3)) ; creation du vecteur      (setf (aref v 0) 1) ; v[0]:= 1      (aref v 0) ; -> 1      

Exemple de création d'un tableau à trois dimensions (4 x 2 x 3) et initialisé :

       (make-array '(4 2 3) :initial-contents                   '(((a b c) (1 2 3))                    ((d e f) (3 1 2))                    ((g h i) (2 3 1))                    ((j k l) (0 0 0))))      

... cela retourne :

      #3A(((A B C) (1 2 3)) ((D E F) (3 1 2)) ((G H I) (2 3 1)) ((J K L) (0 0 0)))      

Registres

Exemples :

      (defstruct livre auteur titre) ; construit une structure "livre" contenant trois champs      (make livre) ; construit la structure en mémoire      (setq l (make-livre :auteur 'Hugo)) ; crée un livre dont l'auteur est Hugo et l'associe à la variable l             (setf (livre-titre l) 'Miserables) ; associe un titre au livre de Hugo      (livre-titre l) ;;-> Miserables      

Tables de hachage

Les tables de hachage stockent des associations entre objets. N'importe quel type d'objet peut être utilisé comme clef ou valeur. Les tables de hachage, comme les tableaux, sont automatiquement redimensionnées si nécessaire.

Paquetages

Les paquetages (packages) sont des collections de symboles, utilisés principalement pour partitionner un programme en espaces de noms. Un paquetage 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 (principe de l'encapsulation) sont 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. Les caractéristiques saillantes de CLOS sont les suivantes :

  • c'est un système à classes (il existe en effet des systèmes à prototypes),
  • il permet l'héritage multiple entre classes,
  • 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 (Meta Object Protocol ou MOP), lié à l'existence des méta-classes, et permettant de modifier la sémantique et le comportement du système,
  • 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 paramètres obligatoires (et non pas d'un receveur privilégié comme dans les langages à sélection simple, qui sont la très grande majorité) ; les méthodes de CLOS sont regroupées dans des fonctions génériques au lieu d'appartenir à des classes,
  • il permet la combinaison de méthodes, c’est-à-dire la définition de méthodes auxiliaires 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.

Certaines implémentations proposent en extension de CLOS un protocole à méta-objets décrit par le livre The Art of the Metaobject Protocol.

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 paramètre, 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 paramètre. 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) (< (first 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 gauche à droite 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 paramètres. Par exemple, l'opérateur if n'évalue pas tous ses paramètres, il doit évaluer sa condition et puis en fonction du résultat, une branche de l'alternative.

Capture lexicale

Une fermeture lexicale est une fonction dont les variables libres capturent les liaisons de l'environnement lexical dans lequel elles sont définies. 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èmes de gestion d'exceptions les plus complets ; 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.

Page générée en 0.140 seconde(s) - site hébergé chez Contabo
Ce site fait l'objet d'une déclaration à la CNIL sous le numéro de dossier 1037632
A propos - Informations légales | Partenaire: HD-Numérique
Version anglaise | Version allemande | Version espagnole | Version portugaise