Common Lisp, implémentation GNU de Lisp

LISP est un des premiers langages de programmation, né en 1958, peu après FORTRAN. Très flexible, il est très utilisé en intelligence artificielle. Sa syntaxe est très régulière.
Modif. 2021.07.22

0. Présentation

0.1 Utiliser clisp
0.2 Introduction

1. Types

1.1 Assignations
1.2 Nombres
1.3 Chaînes (à faire)
1.4 Listes
1.5 Tableaux indexés
1.6 Tableaux associatifs

2. Définitions de fonction

3. Conditions et boucles

3.1 Booléens
3.2 Opérateurs logiques
3.3 Conditions
3.4 Boucles

4. Entrées / sorties

4.1 Entrées
4.2 Sorties
4.3 Fichiers (à faire)

0. Présentation

Il s'agit de notes personnelles, écrites en espérant qu'elles puissent servir d'introduction du langage. Au lieu de restranscrire la documentation officielle, tout a été expérimenté, les exemples fonctionnant sur la version CLISP 2.49.92 pour GNU/Linux - Debian 10.

0.1 Utiliser clisp

Le nom complet de l'application est «common lisp», parfois abrégé en CL. Il existe dans les distributions Unix courantes, et est peut-être installé conjointement à certaines applications comme emacs.

Pour l'utiliser en mode interactif, ouvrir une console et saisir clisp. On en sort avec (bye), (exit) ou (quit).

Pour lancer un programme écrit en clisp (Unix) :

Pour utiliser clisp dans une console, sans ouvrir le mode interactif :

clisp fichier.lisp lance un fichier écrit en Lisp

clisp -x expression exécute une expression écrite en Lisp. Deux façons de traiter les guillemets doubles :

Note : la séquence !\ doit être évitée.

clisp -c fichier.lisp (à préciser!) compile le fichier en byte-code lisible par tous les clisp quelle que soit la plateforme. Ce code est à mi-chemin entre un script clisp et une application en langage-machine.

0.2 Introduction

LE nom du langage Lisp vient de LISt Processor, ce qui signifie que la notion de liste y est centrale. Une autre caractéristique est que toute expression est écrite entre parenthèses, qu'il s'agisse de données, de définitions de fonctions, de calculs… et même les commandes les plus élémentaires : pour quitter le mode interactif (en ligne de commande) il faut écrire (bye), (exit) ou (quit).

Les atomes, expressions les plus simples, comme un nombre, une chaîne… n'existe pas en dehors de parenthèses, il doivent être assemblé en listes. La suivante comportent cinq éléments, tous des atomes :

'(pi "Zoot Allure" 3.14 "Waka/Jawaka" 22/7)

…où pi est un atome représentant une valeur très précise du rapport π. Une liste peut elle-même contenir d'autres listes. La suivant comporte quatre éléments, trois atomes et une liste de deux éléments :

'(pi "Zoot Allure" (3.14 "Waka/Jawaka") 22/7)

Il est possible de mélanger les types de données ; l'espace sert de séparation entre données, atomes ou listes, pas la virgule !

(atom qqch) retourne T (le «vrai» logique) si qqch est un atome, et NIL (le «faux» logique) s'il s'agit d'une liste
(consp qqch) retourne T si qqch est une liste non vide, NIL s'il s'agit d'un atome
(listp qqch) retourne T si qqch est une liste (même vide), et NIL s'il s'agit d'un atome. Le p, signifiant prédicat, est souvent utilisé en fin de fonction pour une réponse T ou NIL.

Sur cette page,

(abs -3) toute parenthèses est évaluée en commençant par la première expression. Le programme cherche ce qu'il doit se passer lorsque l'expression commence avec abs chercher l'atome suivant et en retourner la valeur absolue. Pour la fonction abs, il ne peut y en avoir d'autre, sous peine de déclencher une erreur.

(+ n1 n2 n3 ) est une fonction acceptant un nombre indéfini d'opérandes. Ces derniers peuvent eux-mêmes être le résultat d'autres fonctions :

(+ 5 14 (/ 24 8)) ; addition de 5, 14 et du résultat de la division de 24 par 8

Note : ce qui suit ; est un commentaire, il est ignoré par Lisp

Ces derniers exemples montrent que le Lisp utilise la notation préfixée, aussi appelée notation polonaise, où l'opérateur précède les opérandes.

Pour construire une liste :

(list 1 "3" 4 6 8 9 "quatre")

L'évaluation de cette expression pose problème si un des termes suivant list n'est pas une valeur définie :

(list oncle-Vania 47 "ans")
*** - SYSTEM::READ-EVAL-PRINT: variable ONCLE-VANIA has no value

Il faut alors utiliser la citation (quote(oncle-Vania 47 "ans")) ou son raccourci, l'apostrophe :

'(oncle-Vania 47 "ans")
(ONCLE-VANIA 47 "ans")

1. Types

1.1 Assignations

(setq var1 expr1 var2 expr2 ) affecte des valeurs à des variables. Les expression exprn peuvent être d'autres variables ou résultats de fonctions.

(psetq var1 expr1 var2 expr2 ) permet une affectation en parallèle, permettant par exemple un échange de valeurs entre variables sans variable intermédiaire :

(setq a 1 b 2 c 3)
(psetq a b b c c a) a b c

(setf var 43) affecte une valeur à une variable jusqu'à spécification contraire.

Un nom de variable est toute association de caractères qui ne peut pas être confondue avec une expression numérique. Elle peut en revanche reprendre le nom d'une fonction 

(setf round 43) round (round 3.14)

1.2 Nombres

Organisation des nombres :

mant.exp.exemples
number
    real
        rational
            integer
                fixnum32b-43
                bignum? (expt 10 50000)1
            ratio?22/7
            float
                short-float 13b 5b
                single-float24b 8b
                double-float50b 8b
                long-float50b 8b3.1415926535897932385L0 (pi)
    complexes#C(2.9426026 2.3788464)

1 Sur un i5 à 2.300GHz (2 cores, 4 caches), une vingtaine de secondes ont été nécessaires à l'évaluation du nombre ; (expt 10 100000) dépassait les limites de capacité.

(numberp expr) retourne T si l'expression est un nombre, NIL s'il s'agit d'une chaîne, d'une liste ou d'un opérateur, une erreur dans les autres cas

(evenp n) retourne T si un entier est pair, NIL s'il est impair, erreur pour toute autre expression
(oddp n) retourne T si un entier est impair, NIL s'il est pair, erreur pour toute autre expression
most-positive-fixnum retourne 281474976710655, soit (1- (expt 2 48))
most-negative-fixnum retourne -281474976710656, soit (- 0 (expt 2 48))

(integerp expr) retourne T si un nombre est entier, NIL pour tout autre type de nombre, une chaîne, une liste ou d'un opérateur, une erreur dans les autres cas
(rationalp expr) retourne T si l'expression est un nombre rationnel, NIL s'il s'agit d'un entier, un réel, une chaîne, une liste ou d'un opérateur, une erreur dans les autres cas
(floatp expr) retourne T si l'expression est un nombre réel, NIL s'il s'agit d'un entier, un complexe, une chaîne, une liste ou d'un opérateur, une erreur dans les autres cas
(complexp expr) retourne T si l'expression est un nombre complexe, NIL s'il s'agit de toute autre sorte de nombre, d'une liste ou d'un opérateur, une erreur dans les autres cas
(realp expr) retourne T si l'expression est un nombre réel, NIL s'il s'agit d'un entier, un réel, d'une chaîne, d'une liste ou d'un opérateur ; une erreur dans les autres cas

(symbolp '+) et (symbolp float) retournent T puisque + et float sont des symboles ou des fonctions

Opérateurs

(+ 2 3) donne 5 (lire : l'addition de 2 et 3)
(+ 2 3 1 4 5) les parenthèses permettent de ne marquer qu'un fois l'opérateur : il s'agira ici de la somme de tout ce qui suit l'opérateur +
(- 5 4) soustraction à 5 de 4
(- 2 6 4) à 2 seront successivement soustraits 6 et 4
(- n) retourne l'inverse du nombre

(1+ x) et (1- x) incrémente et décrémente une seule valeur

(* 4 5 6) multiplication de tous les nombres : 120
(/ 51 17) retourne l'entier 3
(/ 24 4 2) le nombre 24 est divisé par 4, et le résultat par 2
(/ 8 24) la division non exacte d'entiers retourne le rationnel : 1/3
(/ 8) retourne l'inverse du nombre : 1/8

Il existe deux façons d'exprimer un réel dans ces deux cas :

(/ 8.0 24) en exprimant un des deux nombres sous une forme réelle : 0.33333334 (sic!)
(float (/ 8 24)) en utiliant la fonction float

(mod n1 n2) ou (rem n1 n2) retourne le modulo, reste de la division de n1 par n2

(round 2.71828) retourne l'entier le plus proche et la différence, parfois négative, c'est-à-dire 3 ; -0.28171992
(round 5 2) retourne l'entier le plus proche du résultat de la division de 5 par 2 puis le reste, c'est-à-dire 3 ; -1

(floor n) arrondit vers l'entier inférieur, c'est-à-dire orienté vers -infini : (floor -3.14) retourne -4 ; 0.8599999
(ceiling n) arrondit vers l'entier supérieur, c'est-à-dire orienté vers +infini : (ceiling -3.14) retourne -3 ; -0.1400001
(truncate n) arrondit à la partie entière : (floor -3.14) retourne -3 ; -0.1400001

floor, ceiling et truncate acceptent un diviseur supplémentaire, voir round().

fround, ffloor, ftruncate et fceiling fonctionnent comme les précédents, mais retournent toujours un dividende réel.

(abs n) retourne la valeur absolue d'un nombre.

Exposants et logarithmes

(isqrt 63) retourne la racine carrée entière 7 (arrondit par défaut) d'une entier
(sqrt 63) retourne la racine carrée réelle 7.937254
(sqrt -63) retourne le complexe #C(0.0 7.937254)

(expt 2 3) retourne 8, 2 exposant 3
(exp 3) retourne 20.085537, exponentielle de base e = 2.7182817
(exp -3) retourne le réel 0.049787067

(log 8) logarithme népérien, naturel, de base e = 2.7182817
(log 8 2) logarithme de base 2

Rapports trigonométriques

pi vaut 3.1415926535897932385L0

sin, cos, tan retournent les rapports trigonométriques directs à partir d'un angle exprimé en radian.

asin, acos, atan sont les fonctions trigonométriques inverses et retournent l'angle exprimé en radian à partir d'une valeur.

Notes : (atan n1 n2) retourne l'angle défini par l'axe des cosinus et le segment [(0, 0), (n2, n1)].

sinh, cosh, tanh retournent les rapports trigonométriques hyperboliques.

asinh, acosh, atanh sont les fonctions trigonométriques hyperboliques inverses.

Complexes

#c(-2.3 2.6) code les nombre complexes, partie réelle à gauche, imaginaire à droite.

(abs #c(2.3 4.5)) retourne l'argument du complexe.

1.3 Chaînes

(stringp expr) retourne T si l'expression est une chaîne, NIL s'il s'agit d'un nombre, d'une liste ou d'un opérateur, une erreur dans les autres cas

(characterp qqch) retourne T si l'expression est un caractère, NIL s'il s'agit d'un nombre, d'une liste ou d'un opérateur, une erreur dans les autres cas

( à suivre )

1.4 Listes

(list 1 2 3) construit une liste
(setf liste7 (list 43 "triton" 121 "seau" "avion")) affecte une liste à la variable liste7

(length liste7) retourne le nombre d'éléments de la liste

(cons qqch liste3) retourne une liste composée de l'élément qqch, atome ou liste, en début de la liste liste3
(cons liste5 liste6) retourne une liste composée d'un premier élément constitué de liste5, suivi des éléments de liste6. Cette nouvelle liste a liste5 pour car et liste6 pour cdr
(cons liste2 atome) retourne une paire séparée par un point composée de liste2, suivi de l'atome

(append liste8 qqch) retourne une liste augmentée d'un élément
(reverse liste9) retourne la liste dont l'ordre des éléments est renversé, sans concerner les éventuelles listes incluses

Ces changements n'affectent une variable liste que si le résultat est explicitement réaffecté à la la variable :

(setq collec (list 1 2 3)) (setq collec (reverse collec))

(push elem collec) ajoute durablement un élément (atome ou liste) en tête de liste

(car collec) retourne le premier élément d'une liste
caar si le premier élément est également une liste, retourne son premier élément ;caaar pour une profondeur de plus, de même pour caaaar

(cdr collec) ou (rest collec) retourne une liste de tous les éléments après le premier
cddr après le second, cdddr après le troisième et cddddr après le quatrième.

caaadr, caadar, caaddr, caadr, cadaar, cadadr, cadar, caddar, cadddr, caddr, cadr, cdaaar, cdaadr, cdaar, cdadar, cdaddr, cdadr, cdar, cddaar, cddadr, cddar ou cdddar existent également, à expérimenter!

first, second, third, fourth, fifth, sixth, seventh, eighth, ninth, tenth et nth n désignent les premier, deuxième, troisième… dixième et nième élements :

(second collec)
triton
(nth 3 collec)
seau

(elem collec n) retourne le nième élément de la liste (le premier est au décalage 0)

(remove val collec) retourne la liste sans l(es) élément(s) dont la valeur est désignée
(find "triton" collec) tourne l'atome, aucune indication sur la place de l'atome ; soit NIL s'il n'existe pas

Attention : remove et find ne fonctionnent pas avec les "chaines".

(member element collec) tourne la liste à partir de la première occurence d'element dans collec, NIL s'il ne s'y trouve pas.

(union collec1 collec2) retourne une liste des éléments de deux listes selon cette procédure : les éléments de la première liste sans les éléments communs sont suivis par tous éléments de la seconde
(nunion collec1 collec2) ne garantit pas l'intégrité des listes

(intersection collec1 collec2) retourne une liste des éléments communs aux deux listes selon cette procédure : chaque élément de la première liste est conservé s'il se trouve dans la seconde
(nintersection collec1 collec2) ne garantit pas l'intégrité de collec1

(set-difference collec1 collec2) retourne la première liste sans les éléments communs à la seconde
(set-difference collec1 collec2) ne garantit pas l'intégrité de collec1

(set-exclusive-or collec1 collec2) réalise la double différence, ou union - intersection entre deux listes
(nset-exclusive-or collec1 collec2) ne garantit pas l'intégrité des deux listes initiales

(subsetp collec1 collec2) retourne T si les éléments de collec1 se retrouvent tous parmis les éléments de collec2

dolist

dolist permet la récursion de chaque élément d'une liste. Une première expression reprend une variable interne et la liste, une deuxième traite chaque élement :

(setq violette (list 1 2 3 4 5 6 7))
(dolist (x violette) (print x))

mapcar applique une opération à une liste. Dans l'exemple, chaque élément sera inversé :

(mapcar #'/ (list 1 2 3 4))

mapcar peut servir à appliquer une opération binaire sur les éléments appratenant à plusieurs listes (mapcar #'* (list 2 3 4) (list 9 8 7))

Il est possible de définir sa propre fonction.

1.5 Tableaux indexés

Un tableau est un collection de valeurs en une ou plusieurs dimensions ; les valeurs peuvent être de types différents.

(setf tab1dim (make-array '(13))) initialise le tableau unidimensionnel tab1dim à 13 éléments NIL , dont les emplacements sont «numérotés» de 0 à 12

(aref tabl1dim 10) lit la valeur du onzième élément
(setf (aref tabl1dim 1) 43) fixe la valeur du deuxième élément à 43

(vectorp tabl1dim) retourne T puisque les tableaux unidimensionnels sont considérés comme des vecteurs ; NIL pour les tableaux à deux ou plusieurs dimensions.

(setf goban (make-array '(19 19))) initialise le tableau bidimensionnel de 19×19

(aref goban 2 2) lit la valeur du point 3-3
(setf (aref goban 2 2) 1) fixe la valeur du poit 3-3

array-rank-limit retourne le nombre de dimensions possible sur l'implémentation courante. Pour clisp 7.3.0 sur GNU/Linux: 4096
(array-rank goban) retourne la dimension d'un tableau, 2 en l'occurrence
(array-dimensions goban) retourne le nombre d'éléments par dimension d'un tableau, (19 19) en l'occurrence
(array-total-size goban) retourne le nombre total d'éléments d'un tableau, 361 en l'occurrence, (* 19 19)

(arrayp goban) retourne T pour tous les tableaux, quelque dimensions qu'ils aient.

Notes :(setf vec #(1 2 3) est façon plus économique de définir un vecteur (rappel : tableau unidimensionnel)

Vecteurs de bits

(setf ba (make-array '(10 10) :element-type 'bit :initial-element 0)) initialise à 0 les éléments du tableau de bits bidimensionnel ba
(bit ba 3 3) retourne le quatrième élément du quatrième rang du tableau de bits
(setf (sbit ba 3 3) 1) redéfinit le bit indiqué

#*1010 est un bitvector, l'élément 0 est à gauche (le bit le plus significatif)
(bit #*101011 3) retourne le quatrième bit du bitvector

(bit-vector-p #*101110) retourne T si l'argument est un bit-vector.

sbit et simple-bit-vector-p existent également, mais avec quelle nuance?

Valables sur des bit-vectors et des tableaux de bits :

(bit-not #*10) retourne #*01 : inversion des bits

(bit-and #*1100 #*1010) retourne #*1000 : «et» logique
(bit-ior #*1100 #*1010) retourne #*1110 : «ou» inclusif logique
(bit-xor #*1100 #*1010) retourne #*0110 : «ou» exclusif logique
(bit-eqv #*1100 #*1010) retourne #*1001 : équivalence
(bit-nand #*1100 #*1010) retourne #*0111 : inverse du «et»
(bit-nor #*1100 #*1010) retourne #*0001 : inverse du «ou» inclusif
(bit-andc1 #*1100 #*1010) retourne #*0010 : inverse du premier «et» second
(bit-andc2 #*1100 #*1010) retourne #*0100 : premier «et» inverse du second
(bit-orc1 #*1100 #*1010) retourne #*1011 : inverse du premier «ou» second
(bit-orc2 #*1100 #*1010) retourne #*1101 : premier «ou» inverse du second

Pour les opérateurs logiques voir cette section.

1.6 Tableaux associatifs

(pairlis clés données) associe deux listes de même longueur, la première fournissant la clé et la seconde la donnée pour chaque paire ainsi formée.

(setf xx (pairlis '(oiseau cheval chèvre) '(vogel paard gijt))) xx
((OISEAU . VOGEL) (CHEVAL . PAARD) (CHÈVRE . GIJT))

Le même résultat est obtenu en fournissant à la variable les paires déjà toutes formées :

(setf xx '((oiseau . vogel) (cheval . paard) (chèvre . gijt)))

(assoc 'oiseau xx) retourne la paire (oiseau . vogel) à partir de la clé, NIL si la clé n'existe pas
(rplacd (assoc 'chèvre xx) 'geit) remplace gijt par geit dans la paire (chèvre . geit)

(assoc 'paard xx) retourne la paire (cheval . paard) à partir d'une donnée.

Note : les clés sont uniques, mais la même donnée peut être attribuée à plusieurs clés.

2. Définitions de fonction

Il est possible de définir une fonction avec la fonction (defun :

(defun carré (x) (* x x))   ; définition de la fonction
(carré 19)                  ; appel de la fonction

Cette structure commence par (defun suivi du nom de la fonction à définir, suivie de parenthèses prévoyant une variable de réception du paramètre qui sera utilisé dans la définition de la fonction, puis le calcul lui-même, sans oublier la parenthèse fermante de la defunc.

Il n'est pas obligatoire d'être compact, la définition suivante est aussi fonctionnelle :

(defun carré (x)
  (
    * x x
  )
)

Celle-ci est plus classique :

(defun carré (x)
  (* x x))

(symbolp 'carré) retourne T si la fonction carré est définie.

Il est possible de prévoir plusieurs valeurs de réception

(defun hypotenuse (c1 c2)
  (sqrt (+ (* c1 c1) (* c2 c2))))
(hypotenuse 12 5)

Le nom pour chaque variable est arbitraire (cela peut même être le nom d'une fonction) ; ces variables par ailleurs locales, c'est-à-dire qu'elles n'interfèrent pas avec des variables de même nom dans le reste du programme. La preuve, la variable globale c1 définie en dehors de la définition de la fonction hypotenuse n'est pas influencée par la variable locale c1 qui a réceptionné le premier paramètre passé par l'appel de la fonction (hypotenuse 3 4) :

(setf c1 1234) (hypotenuse 3 4) c1

Dans le cas d'une fonction sans paramètre, ne pas oublier les parenthèses vides :

(defun 2pi/3 () (/ (* pi 2) 3))
(2pi/3)

…bien que cette valeur est plus pratique si elle est définie dans une affectation de variable :

(setf 2pi/3 (/ (* pi 2) 3))
2pi/3

À l'intérieur de la définition d'une fonction, (let …) permet de définir une variable locale pour un calcul intermédiaire dont le résultat peut-être réutilisé plusieurs fois. Supposons qu'il faille calculer la différence de volume de deux cubes selon le rapport entre leur côté :

(defun rapVcub (c1 c2) (let ((rc (/ c1 c2))) (* rc (* rc rc))))
(rapVcub 3 5)
27/125

Quelques fonctions

(random n) retourne une valeur selon le type de n :

*random-state* contient une valeur sous forme d'un vecteur de 64 bits

Appels spéciaux de fonctions

(apply #'fonction variable) applique une opération à une variable, en l'occurrence la multiplication des éléments d'une liste :

(setf xx (list 2 3 4 5))
(apply #'* xx)

(reduce #'fonction variable) applique une opération binaire à une liste, en apppliquant le résultat des deux premières valeurs avec la suivante, jusqu'à la dernière. Dans l'exemple, il s'agit de la somme des inverses. 0, le premier élément de la liste, sert d'accumulateur.

(defun +inv (x y) (+ x (/ y)))     ; définition de la fonction binaire +inv
(reduce #'+inv (list 0 1 2 3 4))   ; application aux éléments de la liste

À l'élément 0 est additionné l'inverse de 1 ; à ce résultat l'inverse de 2, etc.

3. Conditions et boucles

3.1 Booléens

Les structures conditionnelles agissent selon une valeur booléenne (insensible à la casse) : le vrai, T, et le faux, NIL ou ().

Beaucoup de fonctions terminant par p (pour prédicat) sont des tests retournant T si vrai et NIL si faux : integerp, arrayp, evenp, etc.

(= n1 n2) teste l'égalité ; il peut y avoir plus de deux nombres à évaluer ; (= 43.0 43) est vrai
(equal n1 n2) teste l'égalité ; (= 43.0 43) est faux
(/= n1 n2) teste l'inégalité de deux ou plusieurs nombres ; T si tous sont différents

< et > si les nombres sont dans une suite strictement inférieure ou strictement supérieure
<= et >= si les nombres se présentent dans une suite inférieure ou égale, ou supérieure ou égale

(equal chaîne1 chaîne2) est insensible à la casse pour 'chaines, pas pour "chaines"
(equalp liste1 liste2) est insensible à la casse

(null expr…) retourne T si l'expression est une liste vide, NIL si elle n'est pas vide ou s'il s'agit d'autre chose

3.2 Opérateurs logiques

Les opérateurs logiques servent à composer des conditions (exps représente deux ou plusieurs expressions) :

(and exps) retourne T si toutes les expressions sont vraies
(or exps) retourne T si au moins une expression est vraie
(xor exps) retourne la seule valeur vraie et son rang (commençant à 1), sinon NIL

(not exp) inverse le booléen représenté par une seule expression. Toute expression autre que () ou NIL est considérée comme T : (not 5) ou (not 0) retournera donc NIL.

Pour les opérateurs logiques appliqués aux bit-vecteurs, voir cette section.

3.3 Conditions

(if (condition) (code-si-oui) (code-si-non)) est la structure conditionnelle la plus connue, universelle, (code-si-non) étant optionnel.

(setf x (read))
(if (< x 5) "plus petit que cinq" "plus grand que quatre")

case est une structure conditionnelle permettent un choix d'action selon une évaluation. Chaque cas est associé à une action. Lorsque l'expression qui suit case correspond à un des cas, le code correspondant est exécuté et le travail de comparaison s'arrête. Il est possible de déterminer un cas toujours vrai (t (code)), souvent en dernier ressort, pour une action par défaut. :

(case (read)  ; (read) attend une saisie au clavier
(1 "un")
(2 "deux")
(3 "trois")
(t "Autre expression"))  ; si le résultat de (read) n'est ni 1 ni 2 ni 3

La troisième structure conditionnelle est un ensemble (comme case) de conditions (comme if), permettant des comparaisons plus sophistiquées. La structure (cond (expressions)) contient une liste de paires ((condition) (actions)) , dont une éventuelle p paire toujours vraie (t (action par défaut)) qui ne se déclenche que si les conditions précédentes n'ont pas été vérifiées :

(defun traitement (x)
(cond ((< x 0) (print "négatif"))
      ((< x 5) (print "naturel inférieur à 5"))
      ((< 4 x 10) (print "de 5 à 9"))
      (t (print "rien de tout cela"))
))
(traitement (read))

3.4 Boucles

Pour passer en revue les éléments d'une liste, voir dolist.

dotimes

dotimes est une boucle permettant de répéter une action, en affectant à une variable locale un nombre défini d'itération :

(dotimes (i 10 "fini!") (print (* i i)))

loop est une boucle infinie à qu'il esst possible de limiter selon une condition amenant à une sortie où l'on veut dans la structure, par exemple :

if

(setf x 0)
(loop (if (> x 3) (return))
   (setf x (1+ x))
   (print x))

when

(setf x 0)
(loop
   (setf x (1+ x))
   (when (= x 3) (return))
   (print x))

…ou if

(setf x 0)
(loop
   (setf x (1+ x))
   (print x)
   (if (= x 17) (return)))

loop peut également provoquer une récurrence entre deux nombres :

(loop for n from 1 to 10 collect n)

4. Entrées / sorties

4.1 Entrées

(read) permet la saisie d'une chaîne de caractères, que l'on peut placer dans une affectation de variable :

(defun carré (x) (* x x))
(carré (read))

…ou directement dans une fonction :

(defun cube (x) (* (* x x) x))
(cube (read))

Il est possible d'entrer une liste en commençant avec une parenthèse ouvrante. La saisie ne pourra avoir lieu qu'après la parenthèse fermante et [Retour-chariot]. Il est donc possible de saisir plusieurs lignes, qui seront assemblées en liste. La fonction list ne doit pas y figurer : elle apparaîtrait sous forme de chaîne. Tout retour-chariot avant la parenthèse de fermeture est considéré comme séparateur entre éléments de la liste, comme les espaces.

4.2 Sorties

(print expr) affiche une expression : nombre, chaîne, liste, variables… avec un retour-chariot préalable et les guillemets autour des chaînes

(prin1 chaîne) affiche une chaîne sans retour-chariot mais avec guillemets

(princ chaîne) affiche une chaîne sans retour-chariot préalable ni les guillemets

format permet un affichage plus précis et sophistiqué :

(format t " ~d fois ~d égale ~d" 7 8 56)
(format t " ~a fois ~a égale ~a" "sept" "huit" "cinquante-six")

…permet d'afficher d'inclure des données dans une chaîne. t permet de ne pas afficher de guillemets autour de la chaîne, nil pour les afficher.

4.3 Fichiers

(à faire)