Premiers pas avec common-lisp

Ressources d’apprentissage Common Lisp

Livres en ligne

Ce sont des livres librement accessibles en ligne.

Références en ligne

  • The Common Lisp Hyperspec est le document de référence du langage pour Common Lisp.
  • The Common Lisp Cookbook est une liste de recettes Lisp utiles. Contient également une liste d’autres sources en ligne d’informations CL.
  • [Common Lisp Quick Reference] (http://clqr.boundp.org/) contient des feuilles de référence Lisp imprimables.
  • Lispdoc.com recherche plusieurs sources d’informations Lisp (Practical Common Lisp, Successful Lisp, On Lisp, l’HyperSpec) pour la documentation.
  • L1sp.org est un service de redirection pour la documentation.

Livres hors ligne

Ce sont des livres que vous devrez probablement acheter ou prêter dans une bibliothèque.

  • [ANSI Common Lisp par Paul Graham] (http://www.paulgraham.com/acl.html).
  • [Common Lisp Recipes par Edmund Weitz] (http://weitz.de/cl-recipes/).
  • Paradigms of Artificial Intelligence Programming a de nombreuses applications intéressantes de Lisp, mais n’est plus une bonne référence pour l’IA.

Communautés en ligne

Bibliothèques

  • Quicklisp est le gestionnaire de bibliothèque pour Common Lisp, et a une longue [liste de bibliothèques prises en charge](https://www.quicklisp .org/beta/releases.html).
  • Quickdocs héberge la documentation de nombreuses bibliothèques CL.
  • Awesome CL est une liste organisée par la communauté de bibliothèques, de frameworks et d’autres trucs brillants triés par catégorie.

Environnements Lisp pré-emballés

Ce sont des environnements d’édition Lisp faciles à installer et à utiliser car tout ce dont vous avez besoin est pré-emballé et préconfiguré.

  • Portacle est un environnement Common Lisp portable et multiplateforme. Il embarque un Emacs légèrement personnalisé avec Slime, SBCL (une implémentation populaire de Common Lisp), Quicklisp et Git. Aucune installation nécessaire, c’est donc un moyen très rapide et facile de démarrer.
  • Lispbox est un IDE (Emacs + SLIME), un environnement Common Lisp (Clozure Common Lisp) et un gestionnaire de bibliothèque (Quicklisp), pré-emballé comme archives pour Windows, MacOSX et Linux. Descendant de “Lisp in a Box” Recommandé dans le livre Practical Common Lisp.
  • Non pré-emballé, mais SLIME transforme Emacs en un IDE Common Lisp, et a un [manuel de l’utilisateur](http://common-lisp. net/project/slime/doc/html/) pour vous aider à démarrer. Nécessite une implémentation Common Lisp distincte.

Implémentations Common Lisp

Cette section répertorie certaines implémentations CL courantes et leurs manuels. Sauf indication contraire, ce sont des implémentations de logiciels libres. Voir aussi la liste des implémentations Common Lisp de logiciels libres de Cliki et la [liste Wikipédia des implémentations commerciales de Common Lisp](https://en.wikipedia. org/wiki/Common_Lisp#Commercial_implementations).

Expressions de base

Essayons une expression de base dans le REPL :

CL-USER> (+ 1 2 3)
6
CL-USER> (- 3 1 1)
1
CL-USER> (- 3)
-3
CL-USER> (+ 5.3 (- 3 2) (* 2 2))
10.3
CL-USER> (concatenate 'string "Hello, " "World!")
"Hello, World!"
CL-USER> 

Le bloc de construction de base d’un programme Common Lisp est le * formulaire *. Dans ces exemples, nous avons des formes de fonctions, c’est-à-dire des expressions, écrites sous forme de liste, dans lesquelles le premier élément est un opérateur (ou une fonction) et le reste des éléments sont les opérandes (c’est ce qu’on appelle la “notation de préfixe”, ou " notation polonaise »). La rédaction de formulaires dans le REPL entraîne leur évaluation. Dans les exemples, vous pouvez voir des expressions simples dont les arguments sont des nombres constants, des chaînes et des symboles (dans le cas de 'chaîne, qui est le nom d’un type). Vous pouvez également voir que les opérateurs arithmétiques peuvent prendre n’importe quel nombre d’arguments.

Il est important de noter que les parenthèses font partie intégrante de la syntaxe et ne peuvent pas être utilisées librement comme dans d’autres langages de programmation. Par exemple, ce qui suit est une erreur :

(+ 5 ((+ 2 4)))
> Error: Car of ((+ 2 4)) is not a function name or lambda-expression. ...

Dans Common Lisp, les formulaires peuvent également être des données, des symboles, des formulaires de macro, des formulaires spéciaux et des formulaires lambda. Ils peuvent être écrits pour être évalués, renvoyant zéro, une ou plusieurs valeurs, ou peuvent être donnés en entrée à une macro, qui les transforme sous d’autres formes.

Bonjour, Nom

Ceci est un exemple légèrement plus avancé qui montre quelques fonctionnalités supplémentaires du lisp commun. Nous commençons avec une simple fonction “Hello, World!” et montrons un développement interactif au REPL. Notez que tout texte entre un point-virgule, ;, et le reste de la ligne est un commentaire.

CL-USER> (defun hello ()
       (format t "Hello, World!~%")) ;We start as before
HELLO
CL-USER> (hello)
Hello, World!
NIL
CL-USER> (defun hello-name (name) ;A function to say hello to anyone
       (format t "Hello, ~a~%" name)) ;~a prints the next argument to format
HELLO-NAME
CL-USER> (hello-name "Jack")
Hello, Jack
NIL
CL-USER> (hello-name "jack") ;doesn't capitalise names
Hello, jack
NIL
CL-USER> (defun hello-name (name) ;format has a feature to convert to title case
       (format t "Hello, ~:(~a~)~%" name)) ;anything between ~:( and ~) gets it
WARNING: redefining COMMON-LISP-USER::HELLO-NAME in DEFUN
HELLO-NAME
CL-USER> (hello-name "jack")
Hello, Jack
NIL
CL-USER> (defun hello-name (name)
       (format t "Hello, ~:(~a~)!~%" name))
WARNING: redefining COMMON-LISP-USER::HELLO-NAME in DEFUN
HELLO-NAME
CL-USER> (hello-name "jack") ;now this works
Hello, Jack!
NIL
CL-USER> (defun hello (&optional (name "world")) ;we can take an optional argument
       (hello-name name)) ;name defaults to "world"
WARNING: redefining COMMON-LISP-USER::HELLO in DEFUN
HELLO
CL-USER> (hello)
Hello, World!
NIL
CL-USER> (hello "jack")
Hello, Jack!
NIL
CL-USER> (hello "john doe") ;note that this capitalises both names
Hello, John Doe!
NIL
CL-USER> (defun hello-person (name &key (number))
       (format t "Hello, ~a ~r" name number)) ;~r prints a number in English
HELLO-PERSON
CL-USER> (hello-person "Louis" :number 16) ;this doesn't quite work
Hello, Louis sixteen
NIL
CL-USER> (defun hello-person (name &key (number))
       (format t "Hello, ~:(~a ~:r~)!" name number)) ;~:r prints an ordinal
WARNING: redefining COMMON-LISP-USER::HELLO-PERSON in DEFUN
HELLO-PERSON
CL-USER> (hello-person "Louis" :number 16)
Hello, Louis Sixteenth!
NIL
CL-USER> (defun hello-person (name &key (number))
       (format t "Hello, ~:(~a [email protected]~)!" name number)) ;[email protected] prints Roman numerals
WARNING: redefining COMMON-LISP-USER::HELLO-PERSON in DEFUN
HELLO-PERSON
CL-USER> (hello-person "Louis" :number 16)
Hello, Louis Xvi!
NIL
CL-USER> (defun hello-person (name &key (number)) ;capitalisation was wrong
       (format t "Hello, ~:(~a~) ~:@r!" name number))
WARNING: redefining COMMON-LISP-USER::HELLO-PERSON in DEFUN
HELLO-PERSON
CL-USER> (hello-person "Louis" :number 16) ;thats better
Hello, Louis XVI!
NIL
CL-USER> (hello-person "Louis") ;we get an error because NIL is not a number
Hello, Louis ; Evaluation aborted on #<SB-FORMAT:FORMAT-ERROR {1006641AB3}>.
CL-USER> (defun say-person (name &key (number 1 number-p)
                                      (title nil) (roman-number t))
       (let ((number (if number-p
                 (typecase number
                   (integer
                (format nil (if roman-number " ~:@r" " ~:(~:r~)") number))
                   (otherwise
                (format nil " ~:(~a~)" number)))
                 "")) ; here we define a variable called number
         (title (if title 
                (format nil "~:(~a~) " title)
                ""))) ; and here one called title
         (format nil "~a~:(~a~)~a" title name number))) ;we use them here

SAY-PERSON
CL-USER> (say-person "John") ;some examples
"John"
CL-USER> (say-person "john doe")
"John Doe"
CL-USER> (say-person "john doe" :number "JR")
"John Doe Jr"
CL-USER> (say-person "john doe" :number "Junior")
"John Doe Junior"
CL-USER> (say-person "john doe" :number 1)
"John Doe I"
CL-USER> (say-person "john doe" :number 1 :roman-number nil) ;this is wrong
"John Doe First"
CL-USER> (defun say-person (name &key (number 1 number-p)
                                      (title nil) (roman-number t))
       (let ((number (if number-p
                 (typecase number
                   (integer
                (format nil (if roman-number " ~:@r" " the ~:(~:r~)") number))
                   (otherwise
                (format nil " ~:(~a~)" number)))
                 ""))
         (title (if title 
                (format nil "~:(~a~) " title)
                "")))
         (format nil "~a~:(~a~)~a" title name number)))
WARNING: redefining COMMON-LISP-USER::SAY-PERSON in DEFUN
SAY-PERSON
CL-USER> (say-person "john doe" :number 1 :roman-number nil) ;thats better
"John Doe the First"
CL-USER> (say-person "louis" :title "king" :number 16 :roman-number nil)
"King Louis the Sixteenth"
CL-USER> (say-person "louis" :title "king" :number 16 :roman-number t)
"King Louis XVI"
CL-USER> (defun hello (&optional (name "World") &rest arguments) ;now we will just
       (apply #'hello-name name arguments)) ;pass all arguments to hello-name
WARNING: redefining COMMON-LISP-USER::HELLO in DEFUN
HELLO
CL-USER> (defun hello-name (name &rest arguments) ;which will now just use
       (format t "Hello, ~a!" (apply #'say-person name arguments))) ;say-person
WARNING: redefining COMMON-LISP-USER::HELLO-NAME in DEFUN
HELLO-NAME
CL-USER> (hello "louis" :title "king" :number 16) ;this works now
Hello, King Louis XVI!
NIL
CL-USER>

Cela met en évidence certaines des fonctionnalités avancées de la fonction format de Common Lisp ainsi que certaines fonctionnalités telles que les paramètres facultatifs et les arguments de mots clés (par exemple, :number). Cela donne également un exemple de développement interactif à un REPL en lisp commun.

Le programme simple Hello World en REPL

Common Lisp REPL est un environnement interactif. Chaque formulaire écrit après l’invite est évalué, et sa valeur est ensuite imprimée à la suite de l’évaluation. Ainsi, le plus simple possible “Hello, World!” programme en Common Lisp est :

CL-USER> "Hello, World!"
"Hello, World!"
CL-USER>

Ce qui se passe ici, c’est qu’une constante de chaîne est donnée en entrée du REPL, elle est évaluée et le résultat est imprimé. Ce que l’on peut voir à partir de cet exemple, c’est que les chaînes, comme les nombres, les symboles spéciaux comme ‘NIL’ et ‘T’ et quelques autres littéraux, sont des formes auto-évaluables : c’est-à-dire qu’elles s’évaluent elles-mêmes.

Expressions Lambda et fonctions anonymes

Une [fonction anonyme][1] peut être définie sans nom via une [Expression Lambda][2]. Pour définir ce type de fonctions, le mot-clé lambda est utilisé à la place du mot-clé defun. Les lignes suivantes sont toutes équivalentes et définissent des fonctions anonymes qui génèrent la somme de deux nombres :

(lambda (x y) (+ x y))
(function (lambda (x y) (+ x y)))
#'(lambda (x y) (+ x y))

Leur utilité est perceptible lors de la création de [formulaires Lambda][3], c’est-à-dire un [formulaire qui est une liste][4] où le premier élément est l’expression lambda et les éléments restants sont les arguments de la fonction anonyme. Exemples ([exécution en ligne][5]) :

(print ((lambda (x y) (+ x y)) 1 2)) ; >> 3

(print (mapcar (lambda (x y) (+ x y)) '(1 2 3) '(2 -5 0))) ; >> (3 -3 3)

[1] : http://www.gnu.org/software/emacs/manual/html_node/elisp/Anonymous-Functions.html#Anonymous-Functions [2] : http://stackoverflow.com/q/13213611/6225838 [3] : http://www.lispworks.com/documentation/HyperSpec/Body/03_ababd.htm [4] : http://www.lispworks.com/documentation/HyperSpec/Body/26_glo_l.htm#lambda_form [5] : http://rextester.com/DNB60705

Somme d’une liste d’entiers

(defun sum-list-integers (list)
    (reduce '+ list))

; 10
(sum-list-integers '(1 2 3 4))

; 55
(sum-list-integers '(1 2 3 4 5 6 7 8 9 10))

Bonjour le monde

Ce qui suit est un extrait d’une session REPL avec Common Lisp dans laquelle un “Hello, World!” fonction est définie et exécutée. Voir les remarques au bas de cette page pour une description plus détaillée d’un REPL.

CL-USER> (defun hello ()
           (format t "Hello, World!~%"))
HELLO
CL-USER> (hello)
Hello, World!
NIL
CL-USER> 

Ceci définit la “fonction” de zéro argument nommée hello, qui écrira la chaîne "Hello, World!" suivie d’une nouvelle ligne vers la sortie standard, et renverra NIL.

Pour définir une fonction on écrit

(defun name (parameters...)
  code...)

Dans ce cas, la fonction s’appelle hello, ne prend aucun paramètre et le code qu’elle exécute consiste à effectuer un appel de fonction. La valeur renvoyée par une fonction lisp est le dernier bit de code de la fonction à exécuter, donc hello renvoie tout ce que (format t "Hello, World!~%") renvoie.

En lisp, pour appeler une fonction, on écrit (nom-fonction arguments...)nom-fonction est le nom de la fonction et arguments... est la liste (séparée par des espaces) des arguments du appel. Il y a des cas particuliers qui ressemblent à des appels de fonction mais qui ne le sont pas, par exemple, dans le code ci-dessus, il n’y a pas de fonction defun qui est appelée, elle est traitée spécialement et définit une fonction à la place.

A la deuxième invite du REPL, après avoir défini la fonction hello, nous l’appelons sans paramètre en écrivant (hello). Cela appellera à son tour la fonction format avec les paramètres t et "Hello, World!~%". La fonction format produit une sortie formatée basée sur les arguments qui lui sont donnés (un peu comme une version avancée de printf en C). Le premier argument lui indique où sortir, avec t signifiant sortie standard. Le deuxième argument lui dit ce qu’il faut imprimer (et comment interpréter les paramètres supplémentaires). La directive (code spécial dans le deuxième argument) ~% indique au format d’imprimer une nouvelle ligne (c’est-à-dire que sous UNIX, il peut écrire \n et sous Windows \r\n). Le format renvoie généralement NIL (un peu comme NULL dans d’autres langages).

Après la deuxième invite, nous voyons que Hello, World a été imprimé et sur la ligne suivante que la valeur renvoyée était NIL.