Programmation 3

Charg´ee de cours : Ir`ene Durand Charg´ede TD : Kahn Le Ngoc Kim

Cours 7 s´eances de 3h : Cours (1) 8h30-11h30, du 15/12, 16/12, 19/12, 20/12 Cours (2) 13h00-16h00 les 14/12, 16/12 et 19/12

http://dept-info.labri.fr/~idurand/Enseignement/PFS/vietnam.html

1 Objectifs

Maˆıtriser un certain nombre de m´ethodes et tech- niques de programmation — symbolique, fonctionnelle — imp´erative, objet dans le but de Ecrire´ des applications — maintenables, r´eutilisables, — lisibles, modulaires, — g´en´erales, ´el´egantes.

Aspects de la programmation non enseign´es :

— Efficacit´eextrˆeme — Temps r´eel — Applications particuli`eres (jeu, image, num´erique, ...)

2 Contenu

Langage support Langage — SBCL: Steele Bank Common Lisp http://www.sbcl. org/ Support de cours — Robert Strandh et Ir`ene Durand : Trait´ede programmation en Common Lisp — Transparents Pourquoi Common Lisp ? — Langage tr`es riche (fonctionnel, symbolique, ob- jet, imp´eratif) — Syntaxe simple et uniforme — S´emantique simple et uniforme — Langage programmable (macros, reader macros) — Repr´esentation de programmes sous la forme de donn´ees 3 — Normalis´epar ANSI — Programmation par objets plus puissante qu’avec d’autres langages Bibliographie

Peter Seibel Apress

Paul Graham : ANSI Common Lisp Prentice Hall

Paul Graham : Advanced Techniques for Common Lisp Prentice Hall

Sonya Keene : Object-Oriented Programming in Common Lisp A programmer’s guide to CLOS Addison Wesley

David Touretzky : Common Lisp : A Gentle introduction to Symbolic Computation The Benjamin/Cummings Publishing Company, Inc 4 Autres documents

The HyperSpec (la norme ANSI compl`ete de Common Lisp, en HTML) http://www.lispworks.com/documentation/HyperSpec/Front/index.htm

SBCL User Manual http://www.sbcl.org/manual/

CLX reference manual (Common Lisp X Interface)

Guy Steele : Common Lisp, the Language, second edition Digital Press, (disponible sur WWW en HTML)

David Lamkins : Successful Lisp (Tutorial en-ligne)

5 Historique de Common Lisp

Langage con¸cu par John McCarthy entre 1956 et 1959 au MIT pour des applications li´ees `al’intelligence artificielle (avec Fortran l’un des plus vieux langages toujours utilis´es)

— Issu de la th´eorie du Lambda-Calcul de Church — Dans les ann´ees 1970, deux dialectes : Interlisp et Maclisp — Aussi : Standard Lisp, NIL, Lisp Machine Lisp, Le Lisp — Travail pour uniformiser les dialectes : Common Lisp — Normalis´epar ANSI en 1994

6 Common Lisp aujourd’hui

Conf´erences — ELS (Bordeaux 08, Milan 09, Lisbone 10, Ham- bourg 11, Zadar 12, Madrid 13, Paris 14, Londres 15, Cracovie 16) http://www.european-lisp-symposium.org/ — Lisp50@OOPSLA http://www.lisp50.org/, 08 — ILC (Standford 05, Cambridge 07, MIT 09, Reno 10, Kyoto 12, Montr´eal 14) http://www.international-lisp-conference.org/10 Forums fr.comp.lang.lisp Chat (avec xchat par exemple) /serveur: irc.freenode.net /join #lisp http://webchat.freenode.net/ #lisp

7 Logiciels/Entreprises utilisant CL — Entreprises et Logiciels commerciaux ITA Software http://www.itasoftware.com Igor Engraver Editeur´ de partition musicales http://www.noteheads.com RavenPack International http://www.ravenpack.com/aboutus/employment.htm — Plate-forme Web : BioCyc Plate-forme d’acc`es BD biologiques (voies m´etaboliques/g´enome) http://www.biocyc.org — Logiciels libres CARMA Case-based Range Management Adviser http://carma.johnhastings.org/index.html BioBike Base de connaissance programmable pour la biologie http://biobike.csbc.vcu.edu OpenMusic Langage visuel pour la composition musicale http://repmus.ircam.fr/openmusic/home GSharp Editeur´ de partitions musicales http://common-lisp.net/project/gsharp Liste de logiciels libres http://www.cliki.net/index

8 Calcul Symbolique num´erique/symbolique

Avec des bits on peut coder des nombres mais aussi des objects de type mot ou phrase

En Lisp, — objects de base : sortes de mots appel´es atomes, — groupes d’atomes : sortes de phrases appel´ees listes. Atomes + Listes = Expressions symboliques (S-expr)

— Lisp manipule des S-expr — un programme Lisp est une S-expr, donc mˆeme repr´esentation pour les programmes et les donn´ees. — Cons´equence : possibilit´ed’´ecrire des programmes qui se modifient ou modifient ou produisent des programmes.

9 Applications de Calcul Symbolique

Toute application non num´erique, en particulier

— Intelligence artificielle (syst`emes experts, interfaces en langages naturel,...) — Raisonnement automatique (preuves de th´eor`emes, preuves de programmes,...) — Syst`emes (impl´ementation de langages, traitement de texte,...) — Calcul formel — Jeux

Voir http://www.cl-user.net

10 Comment faire pour apprendre `a programmer ?

Il faut surtout lire beaucoup de code ´ecrit par des experts.

Il faut lire la litt´erature sur la programmation. Il n’y en a pas beaucoup (peut-ˆetre 10 livres).

Il faut programmer.

Il faut maintenir du code ´ecrit par d’autres personnes.

Il faut apprendre `aˆetre bien organis´e.

11 Standards de codage

Il faut s’habituer aux standards de codage

Pour Common Lisp, suivre les exemples dans la litt´erature, en particulier pour l’indentation de programmes qui est tr`es standardis´ee (et automatis´ee).

Il faut pouvoir comprendre le programme sans regarder les parenth`eses. L’indentation n’est donc pas une question de goˆut personnel.

Il faut utiliser SLIME (Superior Lisp Interaction Mode for Emacs) http://common-lisp.net/project/slime/

12 Common Lisp est interactif

Common Lisp est presque toujours impl´ement´esous la forme de syst`eme interactif avec une boucle d’interaction (read-eval-print loop ou REPL).

Une interaction calcule la valeur d’une S-expression, mais une S-expression peut aussi avoir des effets de bord.

En particulier, un effet de bord peut ˆetre de modifier la valeur d’une variable, de cr´eer une fonction, d’´ecrire sur l’´ecran, dans un flot...

Le langage n’a pas la notion de programme principal. Il est n´eanmoins possible de pr´eciser la fonction `aex´ecuter quand l’application est lanc´ee.

Normalement, on lance Lisp une seule fois par s´eance.

13 Common Lisp est interactif (suite)

Au CREMI, une s´eance est un TD ou une demi-journ´ee de travail. Sur un ordinateur personnel, une s´eance peut durer des mois.

Le langage est con¸cu pour le d´eveloppement interactif. Les instances d’une classes sont mises `ajour quand la d´efinition d’une classe change, par exemple.

La programmation fonctionnelle (sans effets de bord) est elle-mˆeme adapt´ee `al’´ecriture d’applications interactives.

14 Lancer le syst`eme Lisp irdurand@mcgonagall:~$ sbcl This is SBCL 1.0.15, an implementation of ANSI Common Lisp. More information about SBCL is available at .

SBCL is free software, provided as is, with absolutely no warranty. It is mostly in the public domain; some portions are provided under BSD-style licenses. See the CREDITS and COPYING files in the distribution for more information. * 1234

1234 * (+ 3 4)

7 *

15 Quitter le syst`eme Lisp

* hello debugger invoked on a UNBOUND-VARIABLE in thread #

Type HELP for debugger help, or (SB-EXT:QUIT) to exit from SBCL. restarts (invokable by number or by possibly-abbreviated name): 0: [ABORT] Exit debugger, returning to top level.

(SB-INT:SIMPLE-EVAL-IN-LEXENV HELLO #) 0] 0

* (quit) [email protected]:

16 Lisp sous Emacs avec le mode SLIME

Superior Lisp Interaction Mode for Emacs

Beaucoup plus riche que le mode Lisp d’Emacs

Mˆeme aspect et fonctionnalit´es quelque soit le Lisp utilis´e

Pour entrer, M-x Pour sortir, taper une virgule (,) puis quit dans le mini- buffer

— Aide `al’indentation et `ala syntaxe — Compilation interactive de fonctions, de fichiers — Documentation, compl´etion de symboles — D´ebogage

Voir les modes (REPL) et (Lisp Slime) avec c-h m

17 Programmation fonctionnelle

Common Lisp est un langage mixte (fonctionnel, imp´eratif, orient´eobjets) mais ses ancˆetres ´etaient purement fonc- tionnels.

Programmation fonctionnelle : — entit´ede base = fonction — pas d’effets de bord (pas de variables) — structure de contrˆole = si-alors-sinon + r´ecursivit´e

Pour obtenir rapidement des programmes corrects, utiliser la programmation fonctionnelle le plus possible — Les programmes sont plus faciles `atester — Programmation ascendante (bottom-up) — Param`etres sont souvent des fonctions (fermetures) Inconv´enients : efficacit´e

18 Expressions

Un atome peut-ˆetre — un objet auto-´evaluant, — ou un symbole

Une expression (en anglais form) Common Lisp peut ˆetre : — un atome — ou une expression compos´ee (avec des parenth`eses).

19 Expressions, analyse syntaxique

Une expression tap´ee `ala boucle d’interaction est d’abord lue et analys´ee syntaxiquement.

Le r´esultat de cette analyse est une repr´esentation interne de l’expression (S-expression).

La fonction responsable de cette analyse s’appelle read.

Cette fonction est disponible `al’utilisateur.

20 Expressions, ´evaluation

La S-expression est ensuite ´evalu´ee, c’est `adire que sa valeur est calcul´ee.

Cette ´evaluation peut donner des effets de bord.

Le r´esultat de l’´evaluation est un ou plusieurs objets Lisp.

La fonction responsable de l’´evaluation de S-expressions s’appelle eval.

Cette fonction est disponible `al’utilisateur.

21 S-expressions, affichage

Les objets r´esultant de l’´evaluation sont ensuite affich´es (ou imprim´es) en repr´esentation externe.

La fonction responsable de l’affichage s’appelle print.

Cette fonction est disponible `al’utilisateur.

(+ 3 4) 7

Monde extérieur read print Monde intérieur

7

eval

+ 3 4

22 Objets auto-´evaluants

Un objet auto-´evaluant est la mˆeme chose qu’une constante. Le plus souvent, il s’agit de nombres, de caract`eres ou de chaˆınes de caract`eres.

CL-USER> 1234 1234 CL-USER> 6/8 3/4 CL-USER> #\c #\c CL-USER> "bonjour" "bonjour" CL-USER> #(1 2 3 4) #(1 2 3 4)

23 Symboles

Si l’expression est un symbole, il sera consid´er´ecomme le nom d’une variable. la fonction eval va donc renvoyer sa valeur.

CL-USER> *standard-output* # CL-USER> nil NIL CL-USER> t T CL-USER> *features* (:ASDF :SB-THREAD :ANSI-CL :COMMON-LISP :SBCL :UNIX :SB-DOC ...)

24 Expressions compos´ees Une expression compos´ee est une liste de sous-expressions entour´ees de parenth`eses : (op e1 e2 ... en)

Le plus souvent, la premi`ere sous-expression est un sym- bole qui est le nom d’une fonction, d’une macro ou d’un op´erateur sp´ecial.

Les autres sous-expressions sont les arguments de la fonc- tion, de la macro ou de l’op´erateur sp´ecial. Liste des op´erateurs sp´eciaux : block let* return-from catch load-time-value setq eval-when locally symbol-macrolet flet macrolet tagbody function multiple-value-call the go multiple-value-prog1 throw if progn unwind-protect labels progv let quote

25 Expressions compos´ees

CL-USER> (+ 3 4) 7 CL-USER> (length "hello") 5 CL-USER> (+ (* 3 4 2) (- 5 4) (/ 5 3)) 80/3 CL-USER> (if (> 5 4) "oui" "non") "oui" CL-USER> (length *features*) 37 CL-USER> (floor 10.3) 10 0.3000002

Ici, if est un op´erateur sp´ecial, alors que +, *, -, /, >, length sont des fonctions.

26 D´efinition de fonction

L’utilisateur peut d´efinir des fonctions en utilisant la macro defun :

CL-USER> (defun doubler (x) (* 2 x)) DOUBLER CL-USER> (doubler 10) 20 CL-USER> (doubler 3/2) 3 CL-USER> (doubler 1.5) 3.0

27 D´efinition de fonction

CL-USER> (defun my-gcd (x y) (if (= x y) x (if (> x y) (my-gcd y x) (my-gcd x (- y x))))) MY-GCD

Cette indentation est obligatoire, car les programmeurs Lisp ne regardent pas les parenth`eses. De plus, elle doit ˆetre automatique.

28 Op´erateurs bool´eens

Fonction : not

Macros : or, and permettent de former toutes les expressions bool´eennes

CL-USER> (not 3) NIL CL-USER> (or nil (- 2 3) t 2) -1 CL-USER> (and (= 3 3) (zerop 3) t) NIL CL-USER> (and (= 3 3) (zerop 0) t) T CL-USER> (or (and t nil) (or (not 3) 4)) 4

29 D´efinition de variables globales CL-USER> (defvar *x* 1) *X* CL-USER> *x* 1 CL-USER> (defvar *x* 2) *X* CL-USER> *x* 1 CL-USER> (setf *x* 2) 2 CL-USER> (defparameter *y* 1) *Y* CL-USER> *y* 1 CL-USER> (defparameter *y* 2) *Y* CL-USER> *y* 2

Les * autour des noms de variables globales font partie des standards de codage.

30 Expressions avec effets de bord Certains expressions peuvent avoir des effets de bord. Il s’agit par exemple de l’affectation d’une variable ou de l’affichage (autre que par la boucle REP) CL-USER> (setf x 3) 3 CL-USER> x 3 CL-USER> (+ (print 3) 4) 3 7 setf : nom d’une macro (setf x 3) : expression macro (anglais : macro form) print : nom d’une fonction avec un effet de bord (print 3) : expression fonction (anglais : function form).

31 D´efinition de constantes

CL-USER> (defconstant +avogadro-number+ 6.0221353d23) +AVOGADRO-NUMBER+ CL-USER> (setf +avogadro-number+ 89) Can’t redefine constant +AVOGADRO-NUMBER+ . [Condition of type SIMPLE-ERROR]

Les + autour des noms de constantes font partie des stan- dards de codage.

32 Documentation des symboles

CL-USER> (defvar *smic-horaire* 9.67 "smic horaire 01/01/2016") *SMIC-HORAIRE* CL-USER> (documentation ’*smic-horaire* ’variable) "smic horaire 01/01/2016" CL-USER> (defun delta (a b c) "discriminant of a quadratic equation" (- (* b b) (* 4 a c))) DELTA CL-USER> (documentation ’delta ’function) "discriminant of a quadratic equation"

Raccourcis Emacs-Slime c-c c-d d, c-c c-d f ... (voir le mode)

33 Retour sur les Symboles

Constantes : abc, 234hello, |ceci n’est pas un tube|

Sauf avec la syntaxe | ... |, le nom est en majuscules

CL-USER> (defvar abc 22) ABC CL-USER> (defvar 234abc 11) 234ABC CL-USER> (defvar |ceci n’est pas un tube| 8) |ceci n’est pas un tube| CL-USER> (+ |ceci n’est pas un tube| 3) 11

34 Quote

Souvent, en programmation symbolique, le symbole n’est utilis´eque pour son nom.

On ne lui attribue pas de valeur.

Comment affecter `aun symbole s1, un symbole s2 et non pas la valeur du symbole s2 ?

La solution est un op´erateur sp´ecial appel´e quote

De mani`ere g´en´erale, quote empˆeche l’´evaluation de son argument (expression ou atome)

CL-USER> (defparameter s1 (quote s2)) s1 CL-USER> s1 s2

35 Quote

CL-USER> (quote hello) HELLO CL-USER> (defparameter *symbole* (quote |ceci n’est pas un tube|)) *SYMBOLE* CL-USER> *symbole* |ceci n’est pas un tube|

Au lieu de taper (quote expr) on peut taper ’expr.

CL-USER> ’hello HELLO CL-USER> (setf *symbole* ’hello) HELLO CL-USER> ’(+ 1 2 3) (+ 1 2 3)

36 Symboles

Représentation: name "ATAN" package

value 234 function # plist

color red

(f a1 a2 a2 ...) : valeur fonctionnelle du symbole f

Autres cas (+ f g), f : valeur de la variable f

37 Symboles

CL-USER> length Error in KERNEL::UNBOUND-SYMBOL-ERROR-HANDLER: the variable LENGTH is [Condition of type UNBOUND-VARIABLE] CL-USER> (setf length 100) Warning: Declaring LENGTH special. 100

Si on veut la fonction associ´ee `aun symbole dans une autre position que suivant une parenth`ese ouvrante : op´erateur sp´ecial function ou raccourci #’.

CL-USER> (function length) # CL-USER> #’length #

38 Comparaison de symboles

Tester l’´egalit´eentre deux symboles est une op´eration tr`es rapide.

Tableau de hachage dans le paquetage (package) courant.

C’est la fonction read qui cherche dans le tableau de ha- chage et ´eventuellement cr´ee le symbole.

CL-USER> (defvar *c* ’|ceci n’est pas un tube|) *C* CL-USER> (eq *c* ’|ceci n’est pas un tube|) T CL-USER> (eq *c* ’hello) NIL

39 Conditionnelles (1)

(if (f ...) (g ...) "hello")

(cond ((> x 3) (setf y (g ...)) (+ x y)) (finished (+ x 15)) (t 0))

(case (f ...) ((apr jun sept nov) 30) (feb (if (leap-year) 29 28)) (t 31))

Comparaison des cl´es avec le pr´edicat eql

40 D´efinition de variables locales

Un contexte est une suite d’instructions dans un environ- nement d´efinissant des variables locales

(defun f (n) (let ((v1 (sqrt n)) (v2 (log n))) (* (+ v1 v2) (- v1 v2))))

(let* ((v1 (sqrt n)) (v2 (log v1))) (* (+ v1 v2) (- v1 v2)))) la derni`ere expression est ´equivalente `a:

(let ((v1 (sqrt n))) (let ((v2 (log v1))) (* (+ v1 v2) (- v1 v2)))) 41 Objets de premi`ere classe

Un objet est de premi`ere classe s’il peut ˆetre : la valeur d’une variable, l’argument d’un appel de fonction et re- tourn´epar une fonction.

Dans la plupart des langages les types de base (nombres, caract`eres,...) sont des objets de premi`ere classe.

En Lisp, les fonctions sont des objets de premi`ere classe.

CL-USER> #’1+ # CL-USER> (mapcar #’1+ ’(4 3 4 8)) (5 4 5 9) CL-USER> (reduce #’ ’(4 3 5 8)) 8

42 Fonctions anonymes ou Abstractions

CL-USER> (lambda (x) (+ x 2)) # CL-USER> (mapcar (lambda (x) (+ x 2)) ’(3 4 5 6)) (5 6 7 8) CL-USER> (find-if (lambda (x) (> x 5)) ’(5 8 3 9 4 2) :from-end t) 9 CL-USER> (defparameter *l* (list "fait" "il" "chaud")) *L* CL-USER> (sort *l* (lambda (x y) (> (length x) (length y)))) ( "chaud" "fait" "il") CL-USER> *l* ("il")

43 Fonctions anonymes(suite)

CL-USER> (complement #’<) #

Comment appeler une fonction anonyme sans lui donner de nom ?

On ne peut pas ´ecrire ((complement #’<) 1 3)

Il faut utiliser la fonction funcall :

CL-USER> (funcall (complement #’<) 1 3) NIL CL-USER> (funcall (lambda (x) (* x x)) 5) 25

44 Fonction retournant une fonction anonyme

CL-USER> (defun composition (f g) (lambda (x) (funcall g (funcall f x)))) COMPOSITION CL-USER> (composition #’sin #’asin) # CL-USER> (funcall (composition #’sin #’asin) 1) 0.99999994 CL-USER>

45 Fonctions nomm´ees Les fonctions nomm´ees sont des fonctions anonymes as- soci´ees `a(la valeur fonctionnelle d’)un symbole.

On peut nommer automatiquement une fonction en la d´efinissant avec la macro defun.

CL-USER> (defun plus-deux (x) (+ 2 x)) PLUS-DEUX CL-USER> (plus-deux 4) 6 ou manuellement en affectant le champs fonction du sym- bole

CL-USER> (setf (symbol-function ’plus-trois) (lambda (x) (+ 3 x))) # CL-USER> (plus-trois 4) 7 46 Paires point´ees

La paire point´ee ou le cons est le type de base qui va servir `aconstruire des listes ou des structures arborescentes.

paire pointée

e1 e2

Op´erations : constructeur cons, accesseurs car, cdr

CL-USER> (cons ’a ’b) (A . B) CL-USER> (defparameter *p* (cons ’a ’b)) *P* CL-USER> (car *p*) A CL-USER> (cdr *p*) B

47 Listes Une liste est soit la liste vide () ou nil, soit une paire point´ee donc le cdr est une liste.

nil 1 nil "abc" xyz

1 nil "abc" xyz

Affichage par print : (1 NIL "abc" XYZ) Une liste g´en´eralis´ee (anglais : dotted list) est termin´ee par un objet autre que nil.

"hello"

1 "abc"

Affichage par print : (1 "abc" . "hello")

48 Listes (suite)

Une liste tap´ee `ala boucle REPL est consid´er´ee comme une expression compos´ee, et sera donc ´evalu´ee. Pour ob- tenir une liste sans l’´evaluer, utiliser quote.

CL-USER> (+ 3 4) 7 CL-USER> ’(+ 3 4) (+ 3 4) CL-USER> (defvar *l* ’(+ 3 4)) *L* CL-USER> *l* (+ 3 4)

49 Listes (suite)

Op´erations de base : cons, car, cdr

CL-USER> (cons 1 (cons 2 (cons 3 nil))) (1 2 3) CL-USER> (cons ’hello ’(how are you)) (HELLO HOW ARE YOU) CL-USER> (setf *l* ’(how are you)) (HOW ARE YOU) CL-USER> (cons ’hello *l*) (HELLO HOW ARE YOU) CL-USER> *l* (HOW ARE YOU) CL-USER> (car *l*) HOW CL-USER> (cdr *l*) (ARE YOU) 50 Listes (suite)

Op´erations plus complexes : list, append, reverse, ...

CL-USER> (defparameter *a* ’hello) *a* CL-USER> (setf *l* ’(*a* 3 (+ 1 4))) (*A* 3 (+ 1 4)) CL-USER> (setf *l* (list *a* 3 (+ 1 4))) (HELLO 3 5) CL-USER> (append *l* ’(a b c)) (HELLO 3 5 A B C) CL-USER> *l* (HELLO 3 5) CL-USER> (reverse *l*) (5 3 HELLO) CL-USER> *l* (HELLO 3 5) 51 Listes (suite)

Structure de contrˆole de base : la r´ecursivit´e

Utiliser endp pour terminer la r´ecursion

(defun greater-than (l x) (if (endp l) ’() (if (> (car l) x) (cons (car l) (greater-than (cdr l) x)) (greater-than (cdr l) x))))

CL-USER> (greater-than ’(5 3 6 4 5 3 7) 4) (5 6 5 7)

52 Listes (suite)

Mais on n’a presque jamais besoin de r´ecursion sur les listes.

CL-USER> (remove-if-not (lambda (x) (> x 4)) ’(5 3 6 4 5 3 7)) (5 6 5 7) CL-USER> (some (lambda (x) (and (zerop (rem x 3)) x)) ’(1 3 5 7)) 3 CL-USER> (every #’oddp ’(1 3 5 7)) T

Attention :

CL-USER> (car nil) NIL CL-USER> (cdr nil) NIL

53 Atomes et listes

(atom x) ≡ (not (consp x))

(listp x) ≡ (or (null x) (consp x))

(endp l) ≡ (null l) mais utiliser endp pour une liste

CL-USER> (atom #(1 2 3)) T CL-USER> (atom "toto") T CL-USER> (atom ’(a)) NIL CL-USER> (listp #(1 2)) NIL CL-USER> (listp ’(1 2)) T

54 Listes (suite)

Construction : cons, list, list*, append, copy-list, copy-tree, revappend, butlast, ldiff, subst, subst-if, subst-if-not, adjoin,union, intersection, set-difference, set-exclusive-or, subsetp

Acc`es : car, cdr, member, nth, nthcdr, last, butlast, cadadr, cdaaar, ... first, second, ..., tenth

Autres : length, subseq, copy-seq, count, reverse, concatenate, position, find, merge, map, some, every, notany, notevery, search, remove, remove-duplicates, elt, substitute, mismatch

55 R´ecursivit´e terminale Un appel est dit terminal si aucun calcul n’est effectu´e entre son retour et le retour de la fonction appelante

Une fonction r´ecursive est dite r´ecursive terminale, si tous les appels r´ecursifs qu’elle effectue sont terminaux.

Fonction RT ⇒ pas besoin d’empiler les appels r´ecursifs

Si r´ecursion lin´eaire, transformation possible en RT

Le plus souvent, ajout d’argument(s) jouant un rˆole d’accumulateur

(defun fact-aux (n p) (if (zerop n) p (fact-aux (1- n) (* n p))))

(fact-aux 6 1) 56 Fonctions funcall, apply, reduce funcall function &rest args+ => result* CL-USER> (funcall #’+ 3 5 2 7 3) 20 apply function &rest args+ => result* CL-USER> (apply #’+ ’(3 5 2 7 3)) 20 CL-USER> (apply #’+ ’()) 0 CL-USER> (apply #’+ 3 5 ’(2 7 3)) 20 reduce function sequence &key key from-end start end initial-value => result CL-USER> (reduce #’cons ’(1 2 3 4 5)) ((((1 . 2) . 3) . 4) . 5) CL-USER> (reduce #’cons ’(1 2 3 4 5) :from-end t :initial-value nil) (1 2 3 4 5) CL-USER> (reduce #’cons ’(0 1 2 3 4 5 6) :start 2 :end 5) ((2 . 3) . 4)

57 Fonctions d’application fonctionnelles mapcar, mapcan

CL-USER> (mapcar (lambda (x) (list (* 2 x))) ’(1 2 3 4)) ((2) (4) (6) (8)) CL-USER> (mapcan (lambda (x) (list (* 2 x))) ’(1 2 3 4)) (2 4 6 8) CL-USER> (mapcar #’append ’((1 2) (3 4) (5 6)) ’((a b) (c d))) ((1 2 A B) (3 4 C D)) CL-USER> (mapcan #’append ’((1 2) (3 4) (5 6)) ’((a b) (c d))) (12AB34CD)

58 Listes d’association Une liste d’association est une liste de paires (cl´e,valeur). CL-USER> (defparameter *la* ’((blue . bleu) (red . rouge) (yellow . jaune))) *la* CL-USER> (assoc ’red *la*) (RED . ROUGE) CL-USER> (assoc ’green *la*) NIL CL-USER> (defparameter *la1* ’(("un" . 1) ("deux" . 2) ("trois" . 3))) *LA1* CL-USER> (assoc "un" *la1*) NIL CL-USER> (assoc "un" *la1* :test #’equal) ("un" . 1) Fonctions sp´ecifiques : assoc, acons, assoc-if, copy-alist, assoc-if-not, rassoc, rassoc-if, rassoc-if-not, pairlis, sublis

59 Egalit´´ es

Pour comparer les valeurs, utiliser : = pour comparer des nombres eql pour les atomes simples (hors tableaux) equal pour les listes et les chaˆınes de caract`eres equalp pour les structures, les tableaux, les tables de hachage = ⊂ eql ⊂ equal ⊂ equalp

CL-USER> (= #C(1 2) #C(1 2)) T CL-USER> (eql nil ()) T CL-USER> (equal ’(1 2 3) ’(1 2 3)) T

Pour tester si deux objects sont identiques, utiliser eq, (en particulier pour les symboles).

60 Egalit´´ es (suite)

CL-USER> (defparameter *x* (list ’a)) *X* CL-USER> (eq *x* *x*) T CL-USER> (eql *x* *x*) T CL-USER> (equal *x* *x*) T CL-USER> (eq *x* (list ’a)) NIL CL-USER> (eql *x* (list ’a)) NIL CL-USER> (equal *x* (list ’a)) T

61 Retour sur la d´efinition et l’appel de fonction Lors d’un appel de fonction tous les arguments sont ´evalu´es. Le passage de param`etres se fait toujours par valeur. Les param`etres se comportent comme des variables lexi- cales (locales).

CL-USER> (defun f (l) (dotimes (i (length l)) (format t "~A:~A " i (car l)) (pop l))) F CL-USER> (defparameter *l* ’(a b c)) *L* CL-USER> (f *l*) 0:A 1:B 2:C NIL 62 Liste des param`etres d’une fonction

4 sortes de param`etres 1. param`etres requis (obligatoires) 2. ´eventuellement param`etres facultatifs, introduits par le mot &optional 3. ´eventuellement : param`etre reste, introduit par le mot &rest 4. ´eventuellement : param`etres mot-cl´es, introduits par le mot &key

CL-USER> (defun g (a b &optional c d) (list a b c d)) G CL-USER> (g 1 2 3) (1 2 3 NIL)

63 CL-USER> (defun h (e &rest l) (list e l)) H CL-USER> (h 1) (1 NIL) CL-USER> (h 1 2 3 4) (1 (2 3 4)) CL-USER> (defun k (&key (couleur ’bleu) image (largeur 10) (hauteur 5)) (list couleur largeur hauteur image)) K CL-USER> (k) (BLEU 10 5 NIL) CL-USER> (k :image ’fleur :hauteur 20) (BLEU 10 20 FLEUR)

64 Macro assert assert test-form [(place*) [datum-form argument-form*]]

CL-USER> (assert (zerop 0)) NIL CL-USER> (assert (zerop 10)) The assertion (ZEROP 10) failed. [Condition of type SIMPLE-ERROR]

Restarts: 0: [CONTINUE] Retry assertion. 1: [ABORT-REQUEST] Abort handling SLIME request. ...

(defun fact (n) (assert (and (integerp n) (> n -1))) (if (zerop n) 1 (* n (fact (1- n))))) 65 Macro assert (suite) Utilisation pour l’´ecriture de fonctions de test Une fonction de test : - retourne NIL si ok - signale une erreur sinon

(defun f (x) (make-list x :initial-element 0))

(defun test-f (x) (let ((l (f x))) (assert (listp l)) (assert (= (length l) x)) (assert (every #’numberp l)) (assert (every #’zerop l))))

CL-USER> (test-f 10) NIL 66 Programmation imp´erative : affectation setf pair* => result* pair := place new-value Affectation de variables Affectation de places CL-USER> (defparameter *v* 10) CL-USER> *v* (setf (car (last *v*)) ’fin) CL-USER> (setf *v* 20) FIN 20 CL-USER> *v* CL-USER> (setf *v* ’(1 2 3)) (1 2 FIN) (1 2 3) CL-USER> (setf (cadr *v*) ’deux) CL-USER> *v* DEUX (1 2 3) CL-USER> *v* CL-USER> (1 DEUX FIN) (setf *v1* 3 *v2* (* 5 *v1*)) 15 CL-USER> (list *v1* *v2*) (3 15)

Affectation parall`ele psetf pair* => nil CL-USER> (psetf *v1* 0 *v2* (* 2 *v1*)) NIL CL-USER> (list *v1* *v2*) (0 6) 67 Op´erations destructives sur les listes rplaca, rplacd, replace, nconc, nreconc, nreverse, push, pushnew, pop, nbutlast, nsubst, fill, delete, delete-duplicates, nsubst-if, nsubst-if-not, nset-difference, nset-exclusive-or, sort, nsubstitute.

68 D´efinitions de fonctions locales (labels)

(defun f (n) (assert (and (integerp n) (> n -1))) (labels ((aux (i fact) (if (zerop i) fact (aux (1- i) (* i fact))))) (aux n 1)))

Port´ee des fonctions locales d´efinies : labels : la fonction est aussi connue dans son propre corps (r´ecursivit´epossible)

69 labels (exemple)

(defun leaves (tree) (let ((l ’())) (labels ((aux (st) (if (atom st) (push st l) (progn (aux (car st)) (aux (cdr st)))))) (aux tree) l)))

70 Hi´erarchie des nombres ratio bignum rational integer fixnum bit

real short−float

single−float float

double−float number

long−float complex

71 Nombres entiers

Pr´ecision arbitraire (bignums)

(defun fact (n) (if (zerop n) 1 (* n (fact (1- n)))))

CL-USER> (fact 5) 120 CL-USER> (fact 40) 815915283247897734345611269596115894272000000000

72 Nombres complexes

Constantes : #c(4 5), #c(1/2 3/4), #c(4/6 2.0)

Les op´erations habituelles marchent exp, expt, log, sqrt, isqrt, abs, phase, signum, sin, cos, tan, cis, asin, acos, atan, sinh, cosh, tanh, asinh, acosh, atanh

CL-USER> (sin (/ pi 6)) 0.49999999999999994d0 CL-USER> (sin #c(3 4)) #C(3.8537378 -27.016813)

Exemples d’utilisation : transform´ee de Fourier

73 Nombres flottants CL-USER> 20.03 20.03 CL-USER> 20.4e3 20400.0 CL-USER> 20.4e-3 0.0204 CL-USER> (typep 20.03 ’single-float) T CL-USER> (typep 20.03 ’short-float) T CL-USER> (typep 20.03 ’double-float) NIL CL-USER> (typep 20.4d-3 ’double-float) T CL-USER> (typep 1.0d0 ’double-float) T CL-USER> (typep 1.0s0 ’short-float) T CL-USER>

74 Tableaux CL-USER> (make-array ’(4 3)) #2A((0 0 0) (0 0 0) (0 0 0) (0 0 0)) CL-USER> (make-array ’(4 3) :initial-element 5) #2A((5 5 5) (5 5 5) (5 5 5) (5 5 5)) CL-USER> (defparameter *t* (make-array 4 :initial-contents (list (+ 3 4) "hello" ’hi t))) #(7 "hello" HI T) CL-USER> (aref *t* 1) "hello" CL-USER> (aref (make-array ’(2 2) :initial-contents ’((a1 a2) (b1 b2))) A2 Les vecteurs sont des tableaux `aune dimension. CL-USER> (vectorp *t*) T 75 Valeurs multiples

CL-USER> (floor 75 4) 18 3 CL-USER> (multiple-value-bind (x y) (floor 75 4) (list x y)) (18 3) CL-USER> (values (cons 1 2) ’hello (+ 3 4)) (1 . 2) HELLO 7 CL-USER> (multiple-value-call #’+ (floor 75 4)) 21 CL-USER> (multiple-value-list (floor 75 4)) (18 3)

76 Tables de hachage

Cr´eation d’une table de hachage make-hash-table &key test size rehash-size rehash-threshold => hash-table

CL-USER> (defparameter *ht* (make-hash-table)) *HT* CL-USER> *ht* #

Par d´efaut, les cl´es sont compar´ees `al’aide de eql

Si cl´es pas comparables avec eql, pr´eciser la fonction de comparaison `al’aide du param`etre mot-cl´e test : (make-hash-table :test #’equal) par exemple si les cl´es sont des chaˆınes de caract`eres.

77 Tables de hachage (suite)

Ajout d’entr´ees

CL-USER> (setf (gethash ’weight *ht*) 234) 234 CL-USER> (setf (gethash ’shape *ht*) ’round) ROUND CL-USER> (setf (gethash ’color *ht*) ’nil) NIL

Nombre d’entr´ees : fonction hash-table-count

CL-USER> (hash-table-count *ht*) 3

78 Tables de hachage (suite) Acc`es `aune entr´ee CL-USER> (gethash ’weight *ht*) 234 T ; trouv´e CL-USER> (gethash ’size *ht*) NIL NIL ; pas trouv´e CL-USER> (gethash ’color *ht*) NIL T ; trouv´emais de valeur NIL

Fonction d’application : maphash

CL-USER> (let ((l ’())) (maphash (lambda (k v) (push k l)) *ht*) l) (WEIGHT SHAPE COLOR) 79 Tables de hachage (suite)

Suppression d’une entr´ee CL-USER> (remhash ’toto *ht*) ; cl´einexistante NIL CL-USER> (remhash ’shape *ht*) T ; suppression r´eussie

Macro with-hash-table-iterator CL-USER> (with-hash-table-iterator (suivant *ht*) (do () (nil) ;; boucle infinie (multiple-value-bind (trouve? k v) (suivant) (unless trouve? (return)) (format t "Cle: ~A, Valeur: ~A~%" k v )))) Cle: COLOR, Valeur: NIL Cle: WEIGHT, Valeur: 234 NIL

80 D´efinition de fonctions locales (flet) (defun print-couple (couple s) (format s "[~A,~A]" (first couple) (second couple)))

(defun print-couples (couples &optional (s t)) (dolist (couple couples) (print-couple couple s)))

(defun print-couples (couples &optional (s t)) (flet ((print-couple (couple) (format s "[~A,~A] " (first couple) (second couple)))) (dolist (couple couples) (print-couple couple))))

Port´ee des fonctions locales d´efinies : flet : la fonction n’est connue que dans le corps du flet

81 flet vs labels

(defun polynome (x) (flet ((carre (x) (* x x)) (cube (x) (* x x x))) (+ (* 2 (cube x)) (* 3 (carre x)))))

(defun polynome (x) (labels ((carre (x) (* x x)) (cube (x) (* x (carre x)))) (+ (* 2 (cube x)) (* 3 (carre x)))))

82 It´eration (do ((i 0 (1+ i)) (j n (1- j))) ((<= j 0) (+ x y)) (format t "~a ~a~%" i j) (f i j))

(do* ...)

(dotimes (i 10 (+ x y)) (format t "~a~%" i))

(dolist (elem (mapcar ...) ’done) (format t "~a~%" elem))

Voir aussi sur des listes : mapc, mapl sur des s´equences : map

83 Conditionnelles (2)

(when (f ...) (unless (f ...) (g ...) (g ...) (setf x 23) (setf x 23) (+ x y)) (+ x y)) (typecase ...)

84 Blocs (1)

Trois types de blocs : tagbody, block, progn, prog1, prog2 tagbody est la construction de base, mais n’est jamais uti- lis´ee directement par un utilisateur progn (resp prog1, prog2) ´evalue les expressions en s´equence et retourne la valeur de la derni`ere (resp. premi`ere, deuxi`eme). CL-USER> (progn CL-USER> (prog1 (setf *l* ’(5 2 6 3)) (pop *l*) (setf *l* (sort *l* #’>)) (print *l*)) (car *l*)) (5 3 2) 6 6 CL-USER> *l* (6 5 3 2)

85 Blocs (2) block est comme un progn avec un nom.

CL-USER> (block found (print "hello") (when (> 3 2) (return-from found 23)) (print "hi")) "hello" 23

Certains blocs sont d´efinis implicitement. CL-USER> (f ’(1 2 3 4)) (defun f (l) FIN (dolist (e l ’fin) CL-USER> (f ’(1 2 nil 4)) (when (null e) ECHAP (return-from f ’echap))))

86 Variables

Variable : place m´emoire nomm´ee

Variables CL : dynamiquement typ´ees (type v´erifi´e`al’ex´ecution)

2 sortes de variables : lexicale, sp´eciale

CL-USER> (defvar *x* 10) *X* CL-USER> (setf *x* (1- *x*)) 9 CL-USER> (defparameter *l* ’(1 2 3)) *L*

87 Variables Lexicales

Est lexicale, toute variable qui n’est pas sp´eciale.

Param`etres d’une fonction (sauf si variable sp´eciale) :

(defun f (x y) (+ (* -3 x x) y 4))

Variables locales d´efinies dans un let, let*, do, do* :

(defun my-reverse (l) (let ((rl ’())) (dolist (e l rl) (push e rl))))

88 Variables lexicales : exemples

Variable locale permanente (cf static en C)

CL-USER> (let ((l (list ’do ’mi ’sol))) (nconc l l) (defun note () (pop l))) NOTE CL-USER> (note) DO CL-USER> (note) MI CL-USER> (note) SOL CL-USER> (note) DO

89 Variables Sp´eciales Une variable sp´eciale est globale et dynamique.

Une variable sp´eciale fonctionne comme une Pile.

CL-USER> *print-base* 10 CL-USER> (let ((*print-base* 2)) (format t "~A" 15)) 1111 NIL

CL-USER> (defun affiche-nombre (n *print-base*) (format t "~A" n)) AFFICHE-NOMBRE CL-USER> (affiche-nombre 15 3) 120 NIL 90 Variables Sp´eciales : attention

CL-USER> (defparameter x 10) X CL-USER> (describe ’x) X is an internal symbol in #. It is a special variable; its value is 10. ; No value

(defun f (x) ...) x est sp´eciale !

Par convention, toujours utiliser des noms entour´es d’ast´erisques (*) pour les identificateurs de variables sp´eciales.

*print-base*, *print-circle*, *gc-verbose*.

91 Variables sp´eciales : exemples

(Touresky p156-157)

CL-USER> (defparameter *a* 100) *A* CL-USER> (defun f (*a*) (list *a* (g (1+ *a*)))) F CL-USER> (defun g (b) (list *a* b)) G CL-USER> (f 3) (3 (3 4))

Changer temporairement la valeur d’une variable sp´eciale :

(let ((*standard-output* un-autre-flot)) ...... )

92 Variables lexicales ou sp´eciales CL-USER> (let ((x 10)) (defun f (y) (+ x y))) F CL-USER> (defun g (x) (f x)) G CL-USER> (g 3) 13

Si on avait auparavant d´efini x comme variable sp´eciale

CL-USER> (defparameter x 5) X ... idem ci-dessus CL-USER> (g 3) 6 93 Chaˆınes de caract`eres

Constantes : "abc", "ab\"c", ...

Une chaˆıne de caract`eres est un vecteur, un vecteur est un tableau mais aussi une s´equence.

CL-USER> (aref "abc def" 3) #\Space CL-USER> (lower-case-p (aref "abc def" 4)) T CL-USER> (char< (aref "abc def" 1) #\a) NIL

94 Tableaux ajustables

CL-USER> (setf *tab* (make-array ’(2 3))) #2A((0 0 0) (0 0 0)) CL-USER> (adjustable-array-p *tab*) NIL CL-USER> (setf *tab* (make-array ’(2 3) :adjustable t)) #2A((0 0 0) (0 0 0)) CL-USER> (adjustable-array-p *tab*) T CL-USER> (setf (aref *tab* 0 0) 5) 5 CL-USER> *tab* #2A((5 0 0) (0 0 0)) CL-USER> (adjust-array *tab* ’(3 4)) #2A((5 0 0 0) (0 0 0 0) (0 0 0 0))

95 Vecteurs avec fill-pointer

CL-USER> (setf *tab* (make-array 6 :fill-pointer 4)) #(0 0 0 0) CL-USER> (length *tab*) 4 CL-USER> (setf (fill-pointer *tab*) 6) 6 CL-USER> (length *tab*) 6 CL-USER> (setf *tab* (make-array 6 :fill-pointer 4 :adjustable t)) #(0 0 0 0) CL-USER> (adjust-array *tab* 20 :fill-pointer 10) #(0000000000)

96 Macros

(defmacro pi! (var) (list ’setf var ’pi))

Evaluation´ en deux phases : macro-expansion puis ´evaluation.

Important : macro-expansion faite (par le compilateur) avant la compilation proprement dite.

L’expression : (pi! x) est transform´ee en l’expression : (setf x pi) qui sera compil´ee `ala place de l’expression initiale.

Les macros permettent donc de programmer de nouvelles instructions.

97 Macros (suite)

Exemples de macros pr´ed´efinies : defun, defvar, defparameter, defclass, defmacro, push, pop, incf, decf, loop,...

R´esultat de la macro-expansion : macroexpand-1, macroexpand

CL-USER> (macroexpand-1 ’(incf x)) (LET* ((#:G9838 1) (#:G9837 (+ X #:G9838))) (SETQ X #:G9837)) T CL-USER> (macroexpand-1 ’(pop l)) (LET* ((#:G9836 L)) (PROG1 (CAR #:G9836) (SETQ #:G9836 (CDR #:G9836)) (SETQ L #:G9836))) T

98 Ecrire´ de nouvelles macros

Avec la macro defmacro

CL-USER> (defparameter *x* 0) *x* CL-USER> (defmacro set-x (exp) (list ’setf ’*x* exp)) SET-X CL-USER> (macroexpand-1 ’(set-x (1+ 2))) (SETF *X* (1+ 2)) T CL-USER> (set-x (1+ 2)) 3 CL-USER> *x* 3

99 Backquote

CL-USER> (setf *a* 1 *b* 2) 2 CL-USER> ‘(a is ,*a* and b is ,*b*) (A IS 1 AND B IS 2) CL-USER> (defmacro pi! (var) ‘(setf ,var pi)) PI! CL-USER> (pi! *x*) 3.141592653589793d0 CL-USER> *x* 3.141592653589793d0 CL-USER> (setf *l* ’(1 2 3)) (1 2 3) CL-USER> ‘(the list is ,*l* I think) (THE LIST IS (1 2 3) I THINK)

100 Suppression des parenth`eses avec @ CL-USER> ‘(the elements are ,@*l* I think) (THE ELEMENTS ARE 1 2 3 I THINK)

CL-USER> (defmacro while (test &rest body) ‘(do () ((not ,test)) ,@body)) WHILE CL-USER> (macroexpand-1 ’(while (plusp *n*) (prin1 *n*) (decf *n*))) (DO () ((NOT (PLUSP *N*))) (PRIN1 *N*) (DECF *N*)) T CL-USER> (setf *n* 4) 4 CL-USER> (while (plusp *n*) (prin1 *n*) (decf *n*))) 4321 NIL 101 Exemples

Remarque : quand le param`etre &rest correspond `aune liste d’instructions correspondant au corps d’une instruc- tion, on utilise de pr´ef´erence &body au lieu de &rest.

CL-USER> (defmacro for (init test update &body body) ‘(progn ,init (while ,test ,@body ,update))) FOR CL-USER> (for (setf *i* 4) (>= *i* 0) (decf *i*) (prin1 *i*)) 43210 NIL

102 Difficult´es : capture de variables

;;; incorrect (defmacro ntimes (n &body body) ‘(do ((x 0 (1+ x))) ((>= x ,n)) ,@body))

CL-USER> (let ((x 10)) (ntimes 5 (incf x)) x) 10

CL-USER> (gensym) #:G4551 CL-USER> (gensym) #:G4552 103 Difficult´es : ´evaluations multiples

;;; incorrect malgr´ele gensym (defmacro ntimes (n &body body) (let ((g (gensym))) ‘(do ((,g 0 (1+ ,g))) ((>= ,g ,n)) ,@body)))

CL-USER> (let ((v 10)) (ntimes (decf v) (format t "*"))) ***** NIL

104 Difficult´es

;;; enfin correct (defmacro ntimes (n &body body) (let ((g (gensym)) (h (gensym))) ‘(let ((,h ,n)) (do ((,g 0 (1+ ,g))) ((>= ,g ,h)) ,@body))))

105 Quand ´ecrire une macro

— cr´eation de nouvelles instructions (cf while) — contrˆole de l’´evaluation des arguments (none, once) — efficacit´e(inlining) — ...

Inconv´enients : — difficult´ede relecture du code — se rappeler comment les arguments sont ´evalu´es — ...

106 Exemple : macro defcommand (defvar *commands* (make-hash-table :test #’eq) "table of commands") Nous souhaitons pouvoir faire quelque chose comme ceci : (defcommand com-case ((ligne entier) (colonne entier)) (format t "aller `ala case (~A,~A) ~%" ligne colonne) (values ligne colonne))) qui se traduise en quelque chose qui ressemble `a: (PROGN (DEFUN COM-CASE (LIGNE COLONNE) (FORMAT T "aller `ala case (~A,~A) ~%" LIGNE COLONNE) (VALUES LIGNE COLONNE)) (SETF (GETHASH ’COM-CASE *COMMANDS*) (LAMBDA NIL (COM-CASE (PROGN (PRINC "ligne (entier): ") (READ)) (PROGN (PRINC "colonne (entier): ") (READ)))))) 107 Invocation d’une commande

(defun invoke-command (name) (funcall (gethash name *commands*)))

CL-USER> (invoke-command ’com-case) ligne (entier): 4 colonne (entier): 5 aller `ala case (4,5) 4 5 CL-USER>

108 Vers une solution pour defcommand : CL-USER> (setf *arguments* ’((ligne entier) (colonne entier))) ((LIGNE ENTIER) (COLONNE ENTIER)) CL-USER> (mapcar #’car *arguments*) (LIGNE COLONNE) CL-USER> (mapcar (lambda (arg) (let ((name (car arg)) (type (cadr arg))) ‘(progn (princ ,(format nil "~a (~a): " (string-downcase name) (string-downcase type))) (read)))) *arguments*) ((PROGN (PRINC "ligne (entier): ") (READ)) (PROGN (PRINC "colonne (entier): ") (READ))) 109 Une solution possible pour defcommand : (defmacro defcommand (name arguments &body body) ‘(progn (defun ,name ,(mapcar #’car arguments) ,@body) (setf (gethash ’,name *commands*) (lambda () (,name ,@(mapcar (lambda (arg) (let ((name (car arg)) (type (cadr arg))) ‘(progn (princ ,(format nil ”˜a (˜a): ” (string-downcase name) (string-downcase type))) (read)))) arguments)))))) 110