Cours Scheme
Cours Scheme
Cours Scheme
à Scheme
Notes de cours
Jean-Jacques Girardot
[email protected]
Février 2008
1.1 Introduction
Nous allons présenter dans ce chapitre le langage Scheme, qui est un langage fonctionnel dérivé de Lisp, l’un
des premiers langages pour l’intelligence artificielle. Scheme est défini formellement dans un rapport [25], et de
nombreuses introductions à Scheme sont disponibles, tant sous forme d’ouvrages [28, 17, 27, 1, 3, 2, 8, 9] que sur
l’Internet.
Le langage Scheme est le résultat d’une travail de formalisation et de simplification des diverses versions de
Lisp[10], y compris l’une des normes industrielles, Common Lisp, mais également d’Algol 60 [16].
Lisp est un langage originellement dû à John McCarthy [23], pour lequel de nombreuses implémentations ont
été réalisées ([10, 30, 11, 21, 29]).
Il existe nombre d’ouvrages de référence du langage Scheme, à commencer par la norme du langage ([24, 25]),
des manuels de référence (par exemple [13, 14, 15, 19, 18, 11]), ainsi que des supports de cours divers utilisant le
langage ([9, 28, 17, 27, 8]), le plus connu étant probablement « Structure and Interpretation of Computer Programs »,
[1, 3, 2].
Nous utiliserons ici une mini version du langage Scheme, S CH , écrit en C portable.
Le système S CH a été conçu afin d’être enchassable, c’est-à-dire de pouvoir servir de langage auxiliaire dans
une application nécessitant un langage interne pour la définition de certaines fonctions, un langage de définition de
macro commandes, ou un langage externe pour les interactions.
S CH a été utilisé pour l’écriture d’un processeur d’analyse de textes, pour la construction d’un analyseur XML.
Il est aussi utilisé à l’occasion comme interprète Scheme en stand-alone.
Les langages de programmation devraient être conçus, non en empilant extension sur extension, mais en sup-
primant les faiblesses et les restrictions qui semblent rendre nécessaire l’addition de ces nouvelles extensions.
Scheme démontre qu’un petit nombre de règles de constitution d’expressions, sans restrictions sur la manière
dont ces expressions sont composées, suffisent à définir un langage de programmation pratique et efficace, qui est
suffisamment flexible pour supporter la majorité des paradigmes de programmation en usage aujourd’hui.
1. Ce terme indique ici que ces constructions peuvent être combinées entre elles (pratiquement) sans aucune restrictions.
3
4 CHAPITRE 1. SCHEME, UN LANGAGE FONCTIONNEL
Scheme fut l’un des premiers langages à considérer les procédures 2 comme des citoyens de première classe 3 .
Scheme permet également de disposer de la plupart des concepts des langages fonctionnels, qui sont soit immé-
diatement disponibles dans le langage, soit simples à implanter ; accès à l’environnement, évaluation paresseuse,
continuation, filtrage 4 . . .
La description complète du langage tient en une cinquantaine de pages (le document 5 Revised 5 Report on the
Algorithmic Language Scheme [25] tient lieu de norme, et comporte une description formelle de la syntaxe et de la
sémantique du langage on trouvera également dans [26] une sémantique opérationnelle de Scheme).
Scheme est fondé sur un modèle formel (le lambda calcul – cf. [7], ou encore [6], [20], etc.), si bien que les
programmes Scheme sont pourvus de plein de propriétés sympathiques, et que l’on peut construire des outils fiables
d’analyse et de transformation de code.
Le langage peut être qualifié de fonctionnel en ce sens qu’il offre une complète liberté dans la manipulation
des fonctions, bien qu’il ne présente pas toutes les caractéristiques des « véritables » langages fonctionnels. Scheme
présente aussi de nombreux traits empruntés aux langages impératifs (affectation, effets de bord), tout comme il
offre des constructions permettant de mettre en place des outils de programmation par objets.
#t #f
◦ les nombres ; un nombre est une suite de chiffres, débutant éventuellement par un signe + ou un signe -,
pouvant comporter un point décimal ou un exposant.
2 -45 12.34e-5
◦ les caractères ; un caractère s’écrit sous la forme d’un dièse, #, suivi d’un backslash, \, suivi du caractère ou
du nom du caractère. Voici quelques caractères :
#\a #\+ #\6
◦ les chaînes de caractères ; une chaîne de caractères est une suite arbitraire de caractères placée entre doubles
quotes ". Une double quote doit être précédée du caractère d’échappement \. On en déduit qu’un \ doit aussi
être précédé d’un autre \ pour être digne de figurer dans une chaîne.
"Salut, Monde"
cons
;= #<SUBR :cons :2>
◦ Une liste représente une application fonctionnelle, ou une forme spéciale.
! Une application fonctionnelle consiste en l’application d’une fonction sur ses opérandes. Les éléments de
la liste (constantes, atomes ou autres listes) sont d’abord évalués, dans un ordre non précisé.
Le premier élément de la liste, qui doit être une valeur fonctionnelle, est alors appliqué aux autres élé-
ments. Le résultat de cette application devient le résultat de l’expression symbolique.
! Une forme spéciale est un cas très particulier d’expression Scheme. Les formes spéciales ne sont pas des
applications fonctionnelles. Le premier élément d’une telle forme n’est pas une désignation de fonction,
mais un mot clef. Les autres éléments d’une forme spéciale ne sont pas nécessairement évalués : l’utilisa-
tion qui en est faite dépend de la sémantique associée à la forme spéciale.
Alors que le mécanisme d’exécution d’une application fonctionnelle est toujours le même, celui d’une
forme spéciale est ainsi propre à chaque forme. Heureusement, il n’existe qu’un tout petit nombre de
formes spéciales (c.f. page 7), qui, pour la plupart, correspondent aux instructions de contrôle des langages
impératifs.
1.4.3 Syntaxe
Le langage S CH utilise un sous-ensemble de la notation traditionnelle de Scheme. Les éléments du langage
sont :
identificateurs une suite quelconque de caractères, ne comportant pas les séparateurs du langage.
chaîne une suite de caractères placés entre doubles apostrophes. À l’intérieur d’une chaîne, une double apostrophe
peut être représentée par la séquence \" et le caractère back-slash par la séquence \\.
nombre un nombre s’exprime sous sa forme décimale, une suite de chiffres appartenant à l’intervalle [0-9],
éventuellement précédée du caractère -. Un nombre entier peut aussi s’exprimer sous forme binaire, octale
ou hexadécimale. Il est alors précédé de la suite #b, #o ou #x selon le cas, suivie d’une suite de caractères
appartenant à l’intervalle [01], [0-7], ou [0-9a-zA-Z] selon les cas.
Un nombre peut également se représenter par la séquence #\ suivie d’un caractère ; la valeur est celle du
code ASCII du caractère.
1.5. OPÉRATIONS 7
booléen un booléen représente l’une des valeurs vrai ou faux, qui se notent respectivement #t et #f. Lorsqu’une
valeur booléenne est attendue par une opération du langage, n’importe quelle valeur du langage différente de
faux, #f, est considérée comme vraie.
séparateurs ces caractères spécifiques servent à délimiter les constructions du langage. Les séparateurs sont :
◦ le blanc et les caractères assimilés (tabulation, fin de ligne), qui permettent la séparation des éléments
lexicaux du langage ;
◦ les parenthèses, qui permettent la création de listes, qui elles-mêmes représentent des données ou des
expressions du langage ;
◦ la quote, qui introduit une constante ;
◦ la double quote, qui est le caractère utilisé pour délimiter les constantes de type chaîne ;
◦ le point, utilisé dans la notation des paires pointées ;
◦ le point-virgule, qui introduit un commentaire se terminant à la fin de la ligne ;
◦ le dièse, qui introduit diverses notations (nombres).
1.5 Opérations
1.5.1 Description des opérations
1.5.1.1 Formes spéciales
Une forme spéciale est une liste dont le premier élément est un identificateur qui, dans ce contexte, représente
un mot-clef du langage. La table suivante décrit, parmi les formes spéciales de Scheme, les mots-clefs du langage
S CH.
Nom Description Voir...
=> utilisé avec cond § 1.5.2, p. 9
and expression conditionnelle § 1.5.2, p. 8
begin séquencement § 1.5.2, p. 8
case expression conditionnelle § 1.5.2, p. 8
cond expression conditionnelle § 1.5.2, p. 9
define déclaration de variable § 1.5.2, p. 9
delay expression retardée § 1.5.2, p. 9
do itération non implantée
else utilisé avec cond et case § 1.5.2, p. 9 et § 1.5.2, p. 8
if expression conditionnelle § 1.5.2, p. 10
lambda création de fonction § 1.5.2, p. 10
let création d’environnement local § 1.5.2, p. 10
let* création d’environnement local non implantée
letrec création d’environnement local § 1.5.2, p. 10
or expression conditionnelle § 1.5.2, p. 10
quasiquote quasi quotation non implantée
quote quotation § 1.5.2, p. 11
set ! affectation de variable § 1.5.2, p. 11
unquote quasi quotation non implantée
unquote-splicing quasi quotation non implantée
Les formes spéciales correspondent aux structures de contrôle des langages de programmation traditionnels
(séquence, déclaration, affectation, instructions conditionnelles, etc.).
Notons que cette mini-implantation de Scheme ne propose pas la quasi-quotation (forme spéciales
quasiquote, unquote, unquote-splicing), ni la forme itérative do.
dans laquelle les expi sont des formes S CH, c’est-à-dire des constantes, des symboles ou des listes.
8 CHAPITRE 1. SCHEME, UN LANGAGE FONCTIONNEL
L’interprète S CH évalue ces expressions dans un ordre non spécifié, fournissant un ensemble de valeurs val0 ,
val1 ,. . . valn . La première de ces valeurs doit être une procédure à n paramètres. Cette procédure est alors appliquée
aux arguments val1 ,. . . valn , fournissant le résultat final de l’application.
1.5.1.3 Notes
Les identificateurs utilisés dans le langage Scheme font appel à nombre de caractères habituellement interdits
dans les langages de programmation plus traditionnels : -, ?, !, +, <, >, etc. La norme du langage utilise cette pos-
sibilité pour suggérer un certain nombre de conventions qui facilitent la lecture des programmes. En voici quelques
unes :
◦ le caractère « ! » est utilisé à la fin d’un nom de procédure pour indiquer que cette procédure réalise une
modification physique sur l’un de ces paramètres : set !, set-car !, set-cdr !, reverse !. . .
◦ le caractère « ? » est utilisé à la fin d’un nom de prédure pour indiquer que cette procédure est un prédicat,
c’est-à-dire qu’elle fournit comme résultat une valeur logique, utilisable par exemple pour réaliser un test :
zero ?, positive ?, number ?, null ?. . .
◦ le caractère « - » est utilisé au sein d’un nom représentant un groupe nominal ou un groupe verbal,
afin d’en décomposer la lecture pour la rendre plus lisible : read-char, string-length, et même
call-with-current-continuation. . .
◦ la séquence « -> » indique une conversion d’un type vers un autre : char->integer,
identifier->symbol, string->list, etc.
Il est naturellement conseillé d’utiliser ces conventions lors de la création de variables par l’utilisateur. . .
(and forme forme. . .) La forme spéciale and évalue séquentiellement ses paramètres, jusqu’à en trouver un dont
l’évaluation rendre la valeur false. Dans ce cas, il n’y a pas évaluation des expressions qui suivent, et le résultat de
la forme est la valeur false. Si aucune des expressions ne rend la valeur false, le résultat est la valeur de la dernière
expression exécutée :
(and (> 3 2) #t 6)
;= 6
(and (< 3 2) #t 6)
;= #f
(begin forme forme. . .) La forme spéciale begin évalue séquentiellement ses paramètres. Le résultat est la valeur
de la dernière expression exécutée :
(begin (+ 2 3) (* 5 6))
;= 30
(begin (display "Hello !") (newline) (max 2.3 7.8))
Hello !
;= 7.8
(case clef clause1 clause2 . . .) Dans cette forme spéciale, le premier argument, clef , est une expression quel-
conque. Les autres arguments sont des clauses, dont la syntaxe est :
(liste exp1 exp2 expn )
dans laquelle le premier élément est une liste de valeurs, ou le mot-clef else, et les autres éléments sont expressions
quelconques.
La forme spéciale case évalue en premier l’argument, clef , dont le résultat est une valeur qui sera recherchées
dans la liste des clauses successives de la forme case. Cette recherche s’effectue au moyen de la procédure memv
(le prédicat de comparaison utilisé étant donc eqv ?). Dès que la valeur de clef est trouvée dans l’une des listes,
la clause correspondante est sélectable pour exécution. Une clause est également sélectable si son premier élément
est, non pas une liste, mais le mot-clef else (il convient donc de placer une telle clause en dernier de la liste). La
première clause sélectable est exécutée, c’est-à-dire que les éléments successifs expi sont évalués en séquence, et le
résultat de la dernière évaluation devient le résultat de la forme case.
1.5. OPÉRATIONS 9
(cond clause1 clause2 . . .) Dans cette forme spéciale, les arguments sont des clauses, qui vont être successivement
testées. Dès que le test associé à l’une des clauses est vrai, la clause est exécutée, et son résultat est fourni comme
résultat de la forme cond. Les clauses obéissent à l’une des trois syntaxes suivantes :
Dans la première syntaxe, la forme test est une expression quelconque, et le test de la clause consiste à exécuter
cette expression. Si elle rend vrai, les expi sont exécutées en séquence, et le résultat de la dernière devient le résultat
de la forme cond.
Dans la deuxième syntaxe, la forme test est une expression quelconque, et le test de la clause consiste également
à exécuter cette expression. Si elle rend vrai, c’est-à-dire une valeur différente de #f, la forme fun est exécutée, et
sont résultat, qui doit être une procédure, est appliqué au résultat de la forme test , fournissant à son tour une valeur
qui devient le résultat de la forme cond.
Dans la troisième syntaxe, la clause est toujours vraie ; les expi sont exécutées en séquence, et le résultat de la
dernière devient le résultat de la forme cond. Ce troisième type de clause ne peut apparaître qu’une fois dans une
forme cond, et toujours en dernière position.
Exemples
(define identificateur forme) La forme spéciale define permet la création d’une liaison nom-valeur. Le premier
paramètre de l’opération est un identificateur, le second une expression dont la valeur, après exécution, est affectée
à l’identificateur. Si celui-ci existait déjà, la forme se comporte comme une affectation (c.f. forme set !, §1.5.2, p.
11).
L’opération define admet aussi une simplification de la syntaxe dans le cas de la définition d’une procédure, et la
notation :
est identique à :
(delay forme) La forme spéciale delay accepte comme paramètre une expression qui ne va pas être évaluée,
mais dont le calcul sera retardé. Le résultat de l’opération est une promesse (promise). Cette promesse peut être
évaluée par la procédure force, qui est nilpotente sur tout autre objet :
(if condition forme1 forme2) La forme spéciale if représente une instruction conditionnelle qui admet trois
opérandes, représentant respectivement un test, une forme à évaluer si le test rend vrai (forme1), et une forme à
évaluer si le test rend faux (forme2). Dans tous les cas, seule l’une des deux formes (forme1 ou forme2) est évalué.
(if #t (+ 2 4) (/ 3 0))
;= 6
(if #f (+ 2 4) (/ 3 0))
division par 0
(lambda paramètres forme) La forme spéciale lambda prend comme premier opérande une liste, éventuelle-
ment vide, d’identificateurs, ou un identificateur unique, et comme second opérande une forme quelconque, et
définit une procédure admettant autant de paramètres qu’il y a d’identificateurs dans la liste. Lors de l’appel de
cette procédure avec une liste de valeurs, la forme fournie comme second opérande est exécutée dans le contexte de
définition de la procédure, contexte enrichi d’un ensemble de liaisons correspondant aux identificateurs de la liste
associés aux valeurs d’appel.
Le résultat de l’exécution est donc une valeur fonctionnelle, qui peut être affectée à une variable ou exécutée
directement :
(define toto (lambda (x y) (+ x (* 2 y))))
;= toto
(toto 3 7)
;= 17
((lambda (x y) (+ x (* 2 y))) 3 7)
;= 17
(or forme forme. . .) La forme spéciale or évalue séquentiellement ses paramètres, jusqu’à en trouver un dont
l’évaluation rendre une valeur différente de false. Dans ce cas, il n’y a pas évaluation des expressions qui suivent,
et le résultat de la forme est cette valeur. Si aucune des expressions ne rend la valeur true, le résultat est la valeur de
la dernière expression exécutée :
1.5. OPÉRATIONS 11
(quote forme) La forme spéciale quote prend comme paramètre un objet unique et le retourne sans l’évaluer.
C’est la forme qui permet de désigner un littéral dans le corps d’un programme.
(quote (+ 2 3))
;= (+ 2 3)
(quote toto)
;= toto
(quote (quote (* 5 6)))
;= (quote (* 5 6))
La forme quote admet aussi d’être abrégée sous la forme d’une apostrophe :
’(+ 2 3)
;= (+ 2 3)
’toto
;= toto
”(* 5 6)
;= (quote (* 5 6))
(set ! identificateur forme) La forme spéciale set ! permet la modification d’une liaison nom-valeur. Le premier
paramètre de l’opération est un identificateur, le second une expression dont la valeur, après exécution, est affectée
à l’identificateur. Celui-ci doit avoir été antérieurement défini au moyen d’une forme define, ou encore la forme set !
doit se trouver à l’intérieur d’une forme let ou letrec définissant l’identificateur comme variable locale. Ex :
(define toto 5)
;= toto
toto
;= 5
(set ! toto 8)
;= 8
toto
;= 8
(let ((jojo 3))
(set ! jojo 5)
(* jojo toto))
;= 40
(set ! bobo 3)
Undefined variable : bobo
1.5.3 Prédicats
Les prédicats représentent une classe d’opérations qui rendent des valeurs booléennes, c’est à dire vrai ou faux.
Ces valeurs sont représentées par les notation #t (vrai) et #f (faux). Elles n’ont pas besoin d’être quotées :
’#t
;= #t
#t
;= #t
#f
;= #f
Scheme étant un langage dont les données sont typées, mais non les variables, un premier ensemble de prédicats
permet de tester l’appartenance d’un objet à un type. Ces types sont au nombre de dix, et recouvrent des ensembles
disjoints d’objets : null, booléens, symboles, paires, nombres, caractères, vecteurs, procédures, ports d’entrée-
sortie, chaînes de caractères. Ces prédicats s’appliquent à des objets quelconques du système, et fournissent la
valeur vrai si leur paramètre est du type attendu, la valeur faux sinon.
12 CHAPITRE 1. SCHEME, UN LANGAGE FONCTIONNEL
(null ? objet) Ce prédicat rend vrai si son paramètre est l’objet null, faux sinon.
(null ? ’())
;= #t
(null ? 0)
;= #f
(boolean ? objet) Le résultat est une valeur booléenne, indiquant si le paramètre est une valeur booléenne.
(boolean ? #f)
;= #t
(boolean ? (> 3 4))
;= #t
(boolean ? boolean ?)
;= #f
(symbol ? objet) Le résultat est une valeur booléenne, indiquant si le paramètre est un symbole.
(symbol ? #f)
;= #f
(symbol ? ’hello)
;= #t
(symbol ? 246)
;= #f
(pair ? objet) Ce prédicat rend vrai si son paramètre est une paire, faux sinon.
(number ? objet) Ce prédicat rend true si son paramètre est un nombre, false sinon.
(number ? 7)
;= #t
(number ? 3.25)
;= #t
(char ? objet) Le résultat est une valeur booléenne, indiquant si le paramètre est un caractère.
(char ? 25)
;= #f
(char ? #\A)
;= #t
(char ? "X")
;= #f
(vector ? objet) Le résultat est une valeur booléenne, indiquant si le paramètre est un vecteur.
(vector ? ’Hello)
;= #f
(vector ? #(a b c))
;= #t
(vector ? "hello")
;= #f
1.5. OPÉRATIONS 13
(procedure ? objet) Le résultat est une valeur booléenne, indiquant si le paramètre est une procédure.
(procedure ? ’x)
;= #f
(procedure ? #t)
;= #f
(procedure ? car)
;= #t
(procedure ? vector ?)
;= #t
(procedure ? (lambda (x) (+ x 1)))
;= #t
(port ? objet) Le résultat est une valeur booléenne, indiquant si le paramètre est un port d’entrée-sortie.
(port ? ’hello)
;= #f
(port ? 1)
;= #f
(current-error-port)
;= #<PORT :84>
(port ? (current-error-port))
;= #t
(string ? objet) Le résultat est une valeur booléenne, indiquant si le paramètre est une chaîne de caractères.
(string ? #t)
;= #f
(string ? ’hello)
;= #f
(string ? "hello")
;= #t
(string ? 123)
;= #f
(environment ? objet) Le résultat est une valeur booléenne, indiquant si le paramètre est un environnement.
(environment ? #t)
;= #f
(scheme-report-environment)
;= #<ENV :-151150872 :-151130136>
(environment ? (scheme-report-environment))
;= #t
’25
;= 25
25
;= 25
(+ ’10 ’78)
;= 88
(+ 10 78)
;= 88
Le système sch reconnaît des nombres entiers ou flottants. Ces valeurs numériques sont compatibles avec les repré-
sentations des long ou des double sur les machines sur lesquelles est porté sch.
L’intervalle de définition des nombres entiers est :
[-2147483648 2147483647]
Les nombres entiers peuvent être introduis sous forme décimale, hexadécimale, octale, ou binaires. Le format
s’indique en utilisant explicitement les préfixes #d, #x, #o et #b.
#d1234
;= 1234
#o6234
;= 3228
#x1234
;= 4660
#b10111
;= 23
Les nombres flottants sont notés avec une partie décimale, et/ou avec une partie exponentielle :
125.23
;= 125.23
-5e4
;= -50000
1723e-2
;= 17.23
Un nombre au format entier, hors des limites de représentation des long, est également représenté au format
flottant :
2147483649
;= 2147483649
(number ? objet) Ce prédicat rend true si son paramètre est un nombre, false sinon.
(number ? 7)
;= #t
(number ? 3.25)
;= #t
(integer ? objet) Ce prédicat rend true si son paramètre est un entier, false sinon.
(integer ? 7)
;= #t
(integer ? 3.25)
;= #f
1.5. OPÉRATIONS 15
(<=> nombre nombre) Comparaison des deux nombres passés en opérandes. Le résultat est négatif si le premier
nombre est inférieur au second, nul si les deux nombres sont égaux, positif si le premier nombre est supérieur au
second.
(<=> 3 5.5)
;= -1
(<=> 3 3)
;= 0
(<=> 3 2.5)
;= 1
(/ nombre nombre) Quotient entier des deux nombres passés en opérandes. Si l’un des nombres est flottant, le
résultat est le quotient exact de des deux nombres :
(/ 37 5)
;= 7
(/ 37. 5)
;= 7.4
(remainder nombre nombre) Reste de la division des deux nombres passés en opérandes.
(remainder 7 3)
;= 1
(remainder 7 -3)
;= 1
(remainder -7 3)
;= -1
16 CHAPITRE 1. SCHEME, UN LANGAGE FONCTIONNEL
(remainder -7 -3)
;= -1
(max nombre nombre. . .) La fonction admet un nombre arbitraire d’éléments, et fournit en résultat le plus grand
de ceux-ci. Lorsque la fonction est appelée sans paramètre, elle fournit la plus petite valeur numérique représentable
sur la machine :
(min nombre nombre. . .) La fonction admet un nombre arbitraire d’éléments, et fournit en résultat le plus petit de
ceux-ci. Lorsque la fonction est appelée sans paramètre, elle fournit la plus grande valeur numérique représentable
sur la machine :
(negative ? nombre) Prédicat retournant vrai si son paramètre est négatif, faux sinon.
(positive ? nombre) Prédicat retournant vrai si son paramètre est positif, faux sinon.
(zero ? nombre) Prédicat retournant vrai si son paramètre est nul, faux sinon.
(odd ? nombre) Prédicat retournant vrai si son paramètre est impair, faux sinon.
(even ? nombre) Prédicat retournant vrai si son paramètre est pair, faux sinon.
Scheme propose également la panoplie habituelle de fonctions arithmétiques. Voici la liste de celles qui dont
disponibles dans S CH :
(sin nombre)
(cos nombre)
(tan nombre)
(exp nombre)
(log nombre)
(sqrt nombre)
(asin nombre)
(acos nombre)
(atan nombre) Ces procédures implémentent les fonctions transcendantes (arithmétiques, trigonométriques, etc)
habituelles. Contrairement à ce que prescrit la norme du langage, ces fonctions ne fournissent pas des valeurs
complexes (appels (sqrt -1), (log -1)), mais des nans. Elles sont réalisées, dans l’implantation S CH, par
utilisation de la bibliothèque mathématique du langage C sur des données de type flottant double, et en partagent
donc les qualités et les défauts. . .
1.5. OPÉRATIONS 17
(isinf ? nombre)
(isnan ? nombre) Ces procédures indiquent si leur paramètre est une valeur infinie ou un nan (not a number) :
(log 0)
;= -inf
(log -7)
;= nan
(sqrt -2)
;= nan
(isinf ? (log 0))
;= -1
(isinf ? 3)
;= #f
(isinf ? (log -7))
;= #f
(isnan ? (log -7))
;= 1
(isnan ? (sqrt -2))
;= 1
(isnan ? (log 0))
;= #f
’#\A
;= #\A
#\A
;= #\A
(char ? objet) Ce prédicat indique si son paramètre est un objet de type caractère.
(char ? #\A)
;= #t
(char ? 2.5)
;= #f
(char 65)
;= #\A
(char 233)
;= #\é
(char -23)
;= #\é
(char-downcase char) Cette opération fournit le caractère minuscule correspondant au caractère majuscule passé
en paramètre. Le paramètre doit être un objet de type numérique ou caractère.
(char-downcase #\a)
;= #\A
(char-downcase #\É)
;= #\é
(char-downcase #\Z)
;= #\z
18 CHAPITRE 1. SCHEME, UN LANGAGE FONCTIONNEL
(char-upcase char) Cette opération fournit le caractère majuscule correspondant au caractère minuscule passé en
paramètre. Le paramètre doit être un objet de type numérique ou caractère.
(char-upcase #\a)
;= #\A
(char-upcase #\é)
;= #\É
(char-upcase #\Z)
;= #\Z
(char-alphabetic ? objet) Ce prédicat indique si son paramètre est un objet de type caractère ou numérique repré-
sentant une lettre.
(char-alphabetic ? #\a)
;= #t
(char-alphabetic ? #\+)
;= #f
(char-alphabetic ? 65)
;= #t
(char-alphabetic ? 127)
;= #f
(char-alphabetic ? #t)
char expected : #t
(char-lower-case ? objet)
(char-upper-case ? objet) Ces prédicats indiquent si leurs paramètres sont des objets de type caractère ou numé-
rique représentant une lettre majuscule ou minuscule selon les cas.
(char-upper-case ? #\E)
;= #t
(char-lower-case ? #\e)
;= #t
(char-upper-case ? #\+)
;= #f
(char-upper-case ? 65)
;= #t
(char-lower-case ? "Hello")
char expected : "Hello"
La structure de donnée de base de Lisp est la paire, dite aussi cons ou paire-pointée. Une paire est créée
dynamiquement pas l’opération cons :
(cons objet objet) L’opération prend deux paramètres, des objets quelconques, et fournit une paire nouvelle conte-
nant ces objets :
(cons 3 5)
;= (3 . 5)
(cons "abc" #\A)
;= ("abc" . #\A)
(cons #f 23.45)
;= (#f . 23.45)
Une paire contient deux champs, le car et le cdr. Les opérations car et cdr permettent d’accéder à ces champs :
1.5. OPÉRATIONS 19
(car paire) L’opération prend comme paramètre une paire, et fournit la valeur du car de cette paire.
(cdr paire) L’opération prend comme paramètre une paire, et fournit la valeur du cdr de cette paire.
(set-car ! paire expr) L’opération prend comme premier paramètre une paire, dans laquelle le car va être physi-
quement remplacé par la valeur de la seconde expression :
(set-cdr ! paire expr) L’opération prend comme premier paramètre une paire, dans laquelle le cdr va être physi-
quement remplacé par la valeur de la seconde expression :
(caar paire)
(cadr paire)
...
(cddddr paire) Le langage fournit l’ensemble des combinaisons des opérations car et cdr jusqu’à quatre niveaux.
cadr est ainsi le car du cdr d’une structure, cdddar le cdr du cdr du cdr du car d’une structure, etc. . .
Une notation spécifique existe pour la représentation des paires : le car et le cdr sont placés entre parenthèses,
séparés par un point :
(a . b)
Un tel objet doit être quoté afin d’être reconnu comme une constante :
’(a . b)
;= (a . b)
(pair ? objet) Ce prédicat rend vrai si son paramètre est une paire, faux sinon.
1.5.6.2 Listes
Une liste est une combinaison d’objets du langage Scheme qui répond à la définition suivante : une liste est soit
l’objet spécial null, représentant la liste vide, noté (), soit une paire dont le cdr est une liste.
L’objet null n’a pas besoin d’être quoté dans le langages sch :
’()
;= ()
()
;= ()
Une notation particulière existe pour la représentation des objets du type listes : les éléments d’une liste sont placés
entre parenthèses, séparés les uns des autres par des blancs, des passages à la ligne, ou des ponctuations du langage :
(voici une liste de 6 éléments)
Pour être considérée comme une constante par l’interprète, une liste doit être quotée :
’(1 2 3)
;= (1 2 3)
’(+ 2 3)
;= (+ 2 3)
’((une liste) contenant (d’autres listes))
;= ((une liste) contenant (d (quote autres) listes))
Une liste est, en accord avec sa définition, une structure à base de paires pointées, et peut être ainsi introduite dans
le système :
’(1 . (2 . (3 . ())))
;= (1 2 3)
Lorsque possible, le système essaye de faire apparaître sous forme de liste les structures construites à base de paires
pointées :
(cons 2 (cons (cons 5 (cons 7 ())) (cons 8 (cons 9 10))))
;= (2 (5 7) 8 9 . 10)
La structure créée par cette instruction n’est pas une liste selon notre définition : l’objet le « plus à droite » de la
structure n’est pas l’objet null, mais le nombre 10. Ceci est dénoté par l’apparition d’un point précédant le nombre
dans l’impression du résultat.
(null ? objet) Ce prédicat rend vrai si son paramètre est l’objet null, faux sinon.
(null ? ’())
;= #t
(null ? 0)
;= #f
(append objet objet objet. . .) L’opération crée une nouvelle liste, en plaçant bout à bout les contenus des listes
passées en paramètre, sauf le dernier objet, qui n’est pas recopié, mais devient la fin de la liste.
(append ’(a b c) ’(x y))
;= (a b c x y)
(append ’(a b c) ’(x y) ’z)
;= (a b c x y . z)
(append ’(a b c))
;= (a b c)
Il est possible de créer une copie physique d’une liste au moyen de l’opération append :
(define l ’(a b c d))
;= l
(append l ’())
;= (a b c d)
1.5. OPÉRATIONS 21
(list objet objet objet. . .) L’opération fournit une liste contenant les objets passés en paramètres. L’opération ac-
cepte un nombre quelconque d’éléments.
(list 2 3 (+ 5 7))
;= (2 3 12)
(list "ABC" #\A (> 3 4))
;= ("ABC" #\A #f)
(list)
;= ()
(length objet) Cette procédure fournit, si son paramètre est une liste, la longueur de celle-ci, 0 dans le cas
contraîre.
(length ’(a b c d))
;= 4
(length ’())
;= 0
(length 23)
;= 0
(reverse objet) Cette procédure s’applique à une liste et fournit une nouvelle liste contenant les éléments de la
première en ordre inverse. La fonction ne modifie pas le paramètre :
(define p ’(a b c d e f))
;= p
(reverse p)
;= (f e d c b a)
p
;= (a b c d e f)
(reverse ’())
;= ()
(reverse 23)
pair expected : 23
(reverse ! objet) Cette procédure s’applique à une liste et fournit une nouvelle liste contenant les éléments de
la première en ordre inverse. La fonction transforme physiquement le paramètre, afin de réutiliser les paires le
composant :
(define p ’(a b c d e f))
;= p
(define q (reverse ! p))
;= q
q
;= (f e d c b a)
p
;= (a)
22 CHAPITRE 1. SCHEME, UN LANGAGE FONCTIONNEL
(reverse ! q)
;= (a b c d e f)
q
;= (f)
p
;= (a b c d e f)
(eqv ? exp1 exp2 ) Cette opération teste l’équivalence de ses arguments. Ceux-ci sont considérés comme identiques
s’ils désignent le même objet physique (par exemple, les deux expressions sont des références à la même variable),
le même atome, ou encore des entiers ou caractères de même valeur. Dans certains autres cas, le résultat est laissé
à la discrétion de l’implémentation : deux nombres flottants ou deux chaînes de caractères de même valeur peuvent
être considérés comme égaux, ou non. Voici quelques exemples d’utilisation de la procédure :
(eqv ? (quote toto) (quote toto))
;= #t
(eqv ? 125 125)
;= #f
(eqv ? (* 2 3) (- 8 2))
;= #t
(eqv ? 23.5 23.5)
;= #f
(eqv ? #\a #\a)
;= #t
(define num 125)
;= num
(eqv ? num num)
;= #t
1.5. OPÉRATIONS 23
(equal ? exp1 exp2 ) Cette opération teste l’égalités de ses arguments. Deux objets simples sont égaux s’ils ont
même valeur. Deux listes sont égales si elles ont même longueur et si leurs éléments correspondants sont égaux.
Voici quelques exemples d’utilisation de la procédure :
(equal ? (quote toto) (quote toto))
;= #t
(equal ? (* 2 3) (- 8 2))
;= #t
(equal ? 23.5 23.5)
;= #t
(equal ? #\a #\a)
;= #t
(equal ? "Hello" "Hello")
;= #t
(equal ? ’(a b c d) ’(a b c d))
;= #t
(equal ? ’(a b c) ’(a b c d))
;= #f
langage. L’implantation utilisée, sch, utilise d’ailleurs cette représentation, les références aux objets étant éven-
tuellement optimisées dynamiquement lors de la première exécution d’un code. La notation :
’((a . 1) (b . 2) (c 3 4 5) (d . "Hello"))
;= ((a . 1) (b . 2) (c 3 4 5) (d . "Hello"))
pourrait ainsi représenter une variable a, de valeur 1, une variable b, de valeur 2, une variable c, de valeur (3 4
5), etc.
Trois fonctions permettent de rechercher un couple, à partir de la valeur du car du couple :
(assq expr liste) Ces trois opérations recherchent dans liste un couple dont le car est égal à expr ; elles différent
par la procédure de comparaison utilisée : assoc utilise equal ?, assv utilise eqv ? et assq utilise eq ?.
(assv ’c ’((a . 1) (b . 2) (c 3 4 5) (d . "Hello")))
;= (c 3 4 5)
(cdr (assv ’c ’((a . 1) (b . 2) (c 3 4 5) (d . "Hello"))))
;= (3 4 5)
Les procédures rendent le couple complet ; appliquer la procédure cdr à ce résultat permet d’obtenir la valeur
associée au nom. Les procédures rendent faux si l’objet n’est pas trouvé :
(assv ’g ’((a . 1) (b . 2) (c 3 4 5) (d . "Hello")))
;= #f
(port ? objet) Le résultat est une valeur booléenne, indiquant si le paramètre est un port d’entrée-sortie.
(port ? ’hello)
;= #f
(port ? 1)
;= #f
(current-error-port)
;= #<PORT :84>
(port ? (current-error-port))
;= #t
(current-input-port)
(current-output-port)
(current-error-port) Les résultats de ces opérations sont, respectivement, les ports utilisés pour la lecture des
entrées, l’écriture des sorties et l’écriture des messages d’erreur de l’interprète. Ces ports sont, par défaut, associés
à stdin, stdout et stderr.
(input-port ? objet) Le résultat est une valeur booléenne, indiquant si le paramètre est un port d’entrée-sortie
susceptible de délivrer des caractères.
(current-input-port)
;= #<PORT :52>
(input-port ? (current-input-port))
;= #t
1.5. OPÉRATIONS 25
(output-port ? objet) Le résultat est une valeur booléenne, indiquant si le paramètre est un port d’entrée-sortie
susceptible de recevoir des caractères.
(current-output-port)
;= #<PORT :84>
(output-port ? (current-output-port))
;= #t
(output-port ? (current-input-port))
;= #f
(open-output-string) Le résultats de cette opération est un port, ouvert en écriture, qui est susceptible d’accu-
muler les caractères qui lui sont transmis. La fonction get-port-string permet d’obtenir sous la forme d’une
chaîne de caractères l’ensemble de ces valeurs :
(define x (open-output-string))
;= x
x
;= #<PORT :81>
(write ’hello x)
;= hello
(write ’(a b c) x)
;= (a b c)
(display (+ 2 4) x)
;= 6
(get-port-string x)
;= "hello(a b c)6"
(write "Hello World !" x)
;= "Hello World !"
(get-port-string x)
;= "hello(a b c)6\"Hello World !\""
(open-input-string expr) Le paramètre de cette opération doit être une chaîne de caractères. Le résultat est un
port ouvert en lecture, susceptible de délivrer les caractères successifs de la chaîne ; le port délivre l’objet « fin de
fichier » lorsque l’ensemble des caractères a été lu :
(define s (open-input-string
"125 212 33.450 0000122 1000000000000000e-9 33"))
;= s
s
;= #<PORT :49>
(read s)
;= 125
(read s)
;= 212
(read s)
;= 33.45
(read s)
;= 122
(read s)
;= 1000000
(read s)
;= 33
(read s)
;= #<EOF>
(read s)
26 CHAPITRE 1. SCHEME, UN LANGAGE FONCTIONNEL
;= #<EOF>
(eof-object ? (read s))
;= #t
Les procédures d’entrée-sortie du langage Scheme utilisent, implicitement ou explicitement, des ports comme
sources ou destinations.
(read {port}) Cette procédure lit un objet du langage (symbole, nombre, chaîne, liste, etc) sur le port d’entrée
précisé, par défaut le port associé à stdin. Lorsque le port ne peut plus délivrer de caractères, le résultat est l’objet
« fin de fichier ».
(read-char {port}) Cette procédure lit un caractère sur le port d’entrée précisé, par défaut le port associé à stdin.
Lorsque le port ne peut plus délivrer de caractères, le résultat est l’objet « fin de fichier ».
(force argument) Cette opération retourne la valeur de son argument si celui-ci n’est pas une promesse (elle est
dite alors nilpotente), ou la valeur de l’expression associée à l’argument si celui-ci est une promesse :
Les procédures décrites ici ne font pas partie du standard ; elles sont rendues disponibles à des fins pragmatiques,
ou de test, ou pour tout autre raison. . .
(end) Cette opération interrompt l’exécution de l’interprète et retourne au système d’exploitation, probablement
le shell à partir duquel l’exécution a été lancée. Elle équivaut à l’appel (exit 0).
1.6. SCHEME, EN QUELQUES EXEMPLES 27
(exit argument) Cette opération interrompt l’exécution de l’interprète et retourne au système d’exploitation, en
retournant comme code de retour la valeur de son argument, qui doit être un entier. Une valeur de 0 indique qu’au-
cune erreur ne s’est produite, une autre valeur signale une erreur. Ce code de retour peut être testé dans un shell, est
utilisé par make, etc.
(rand argument) Cette opération fournit un nombre entier pseudo-aléatoire, compris entre 0 (inclus) et son argu-
ment (exclus), qui doit être un nombre entier strictement positif. La fonction utilisée est la fonction Unix RAND(3) :
(rand 1000)
;= 383
(rand 1000)
;= 886
(rand -10)
domain error -10
(rand 0)
domain error 0
(random-seed argument) Cette opération permet de définir la « graine », c’est-à-dire le point de départ de la suite
pseudo-aléatoire générée par les appels successifs à rand. Le paramètre doit être un nombre entier. Le résultat est
la valeur précédente de la graine.
(rand 1000)
;= 383
(rand 1000)
;= 886
(random-seed 1)
;= 1
(rand 1000)
;= 383
(rand 1000)
;= 886
(random-seed 1000)
;= 1
(rand 1000)
;= 790
1.6.1 Tri
Voici un exemple classique de tri par fusion. Le tri d’une liste de nombre s’effectue par l’appel(sort liste
<).
(define (sort l less)
(define (merge l1 l2)
(if (null ? l1)
l2
(if (null ? l2)
l1
(if (less (car l1) (car l2))
(cons (car l1) (merge (cdr l1) l2))
(cons (car l2) (merge l1 (cdr l2)))))))
(define (sort-aux l)
(if (or (null ? l) (null ? (cdr l)))
l
(cut l ’() ’())))
(define (cut l l1 l2)
(if (null ? l)
28 CHAPITRE 1. SCHEME, UN LANGAGE FONCTIONNEL
Nous construisons maintenant trois explorateurs : toto recherche les éléments négatifs, titi les éléments im-
pairs, et tutu les éléments congrus à 1 modulo 5 :
(toto)
;= -9
(titi)
;= 1
(tutu)
;= 1
(list (toto) (titi) (tutu))
;= (-2 3 -9)
(list (toto) (titi) (tutu))
;= (-7 -9 6)
(list (toto) (titi) (tutu))
;= (-3 3 11)
1.6. SCHEME, EN QUELQUES EXEMPLES 29
2.1 Introduction
Ce chapitre décrit une approche à l’interprétation d’un langage fonctionnel, Scheme.
Qu’est-ce qu’un interprète Scheme ? C’est, à la base, une boucle « infinie » dont le corps a pour fonction de lire
une expression, de l’évaluer, et d’imprimer le résultat, ce que nous symboliserons par l’expression :
(write (eval (read)))
qui est souvent désignée (pour des raisons historiques, « print » étant le nom originel de la procédure « write ») sous
le nom de boucle « read-eval-print ».
La procédure read effectue la lecture d’une expression symbolique, dite parfois s-exp (symbolic expression).
Elle a pour tâche de réaliser l’analyse lexicale et syntaxique de l’expression lue, et d’allouer les paires pointées
servant à représenter cette expression (y compris ses sous-expressions).
La procédure eval effectue l’interprétation de la s-exp qui lui est passée en paramètre. Nous étudierons plus
particulièrement son fonctionnement dans ce chapitre.
La procédure write, enfin, imprime son paramètre ; son fonctionnement est aisément modélisable par une
procédure récursive.
Nous allons proposer plusieurs modèles successifs, implémentant les diverses fonctionnalités du langage.
2.2 Modèle 1
Notre première version sera la suivante :
(define (scheme-1)
(display " : ")
(let ((r (eval-1 (read) env)))
(display "=> ")
(write r)
(newline))
(scheme-1))
Cette version (qui réalise l’itération par un appel en récursion terminale), fait appel aux procédures du système
read et write, mais utilise sa propre version de l’évaluateur, qui est la suivante :
(define (eval-1 exp env)
(cond
;; Les constantes manifestes
((or (null ? exp) (boolean ? exp)
(number ? exp) (string ? exp)) exp)
;; Les symboles
((symbol ? exp) (get exp env))
;; Les listes
((list ? exp)
31
32 CHAPITRE 2. IMPLANTATION D’UN LANGAGE FONCTIONNEL : SCHEME
(apply
(eval-1 (car exp) env)
(map (lambda (x) (eval-1 x env)) (cdr exp))))
(else
(error "Expression non interpretable : " exp))))
Notre première version ne fait plus appel à la procédure eval ; elle accepte deux paramètres, une expression sym-
bolique à évaluer, et un environnement. Si l’expression est une constante (telle un nombre, un booléen, etc), la
procédure renvoie directement l’objet. Si c’est un symbole, il représente une variable, dont la valeur va être cher-
chée dans l’environnement fourni (le second paramètre de la fonction). On notera que cette version ne s’intéresse
pas (encore) aux formes spéciales du langage.
Il reste ici à représenter les environnements, et leur manipulation au travers de « get », ainsi que la procédure
« apply ». Les autres procédure utilisées se programment sans difficulté particulière dans un langage tel que C.
Une implémentation de « get » est la suivante :
(define (get sym env)
(let ((v (assq sym env)))
(if (pair ? v)
(cdr v)
(error "Undefined symbol : " sym))))
Cette implémentation fait donc appel à un environnement représenté sous forme de liste associative. Un tel envi-
ronnement peut être représenté par :
(define env (list ’(x . 5) (cons ’+ +)
(cons ’- -) (cons ’* *) (cons ’< <) (cons ’= =) (cons ’> >)
(cons ’<= <=) (cons ’>= >=)
(cons ’cons cons) (cons ’car car) (cons ’cdr cdr)
(cons ’null ? null ?) (cons ’pair ? pair ?) (cons ’list ? list ?)
(cons ’number ? number ?) (cons ’symbol ? symbol ?)
(cons ’string ? string ?) (cons ’boolean ? boolean ?) (cons ’quit quit)
))
Le modèle proposé fait bien sûr appel aux opérations primitives du langage (cons, car, cdr, +, etc), mais on
peut, là aussi aussi, se convaincre que ces opérations sont triviales à implémenter.
Le modèle est directement exécutable ; la « pré-définition » d’une variable, x, permet de tester le système :
(load "evaluator-1.scm")
(scheme-1) ; to run the evaluator
;= #t
(scheme-1)
: x
=> 5
: (+ x 3)
=> 8
: (> (* x 2) 8)
=> #t
: (quit)
L’opération « quit » prédéfinie dans sch interrompt l’exécution courante (donc l’interprète « scheme-1 ») et
provoque un retour au mode terminal.
Notons encore que la procédure « apply », dans la mesure où les paramètres de la fonctions ont été évalués,
se ramène à un simple appel de fonction classique ; on verra comment sa définition doit évoluer pour prendre en
compte les procédures définies du langage.
2.3 Modèle 2
Notre nouvelle version s’intéresse à l’implantation des formes spéciales. Une forme spéciale est simplement
une liste dont l’interprète reconnaît le premier élément comme étant un mot-clef du langage, et à laquelle il associe
une sémantique spéciale. Il convient donc de reconnaître chaque forme spéciale, et d’implémenter une procédure
spécifique pour l’évaluation. C’est ce qui est fait dans notre seconde version du modèle pour les formes « quote »
et « if » :
2.4. MODÈLE 3 33
2.4 Modèle 3
La version 3 du modèle s’intéresse à la définition de variables, et ajoute les formes « define » et « set ! ».
(define (eval-3 exp env)
(cond
; ; Les constantes manifestes
((or (null ? exp) (boolean ? exp)
(number ? exp) (string ? exp)) exp)
; ; Les symboles
((symbol ? exp) (get exp env))
; ; Les listes
((list ? exp)
(cond
; ; Formes speciales ?
((eq ? (car exp) ’quote) (cadr exp))
((eq ? (car exp) ’if)
(if (= 4 (length exp))
(if (eval-3 (cadr exp) env)
(eval-3 (caddr exp) env)
(eval-3 (cadddr exp) env))
(error "if syntax error : " exp)))
((eq ? (car exp) ’define)
(if (and (= 3 (length exp)) (symbol ? (cadr exp)))
(begin
34 CHAPITRE 2. IMPLANTATION D’UN LANGAGE FONCTIONNEL : SCHEME
2.5 Modèle 4
La dernière version du modèle doit maintenant s’attaquer à la définition de procédures. Il suffit en fait de traiter
les cas des lambda-expressions, la forme « define » pour des procédures étant simplement du sucre syntaxique
enveloppant une lambda expression.
Le choix fait ici est de conserver le corps de la lambda expression sous sa forme arborescente. Exécuter une
lambda-expression se ramène tout simplement à évaluer le corps de la lambda-expression dans un environnement
2.5. MODÈLE 4 35
où les variables locales de la lambda-expression ont reçu les valeurs des paramètres. On peut formuler ainsi la chose
(en ne représentant ici que la partie nouvelle du code) :
((eq ? (car exp) ’lambda)
(list xlambda (cadr exp) (caddr exp) env))
Une lambda-expression est donc représentée ici par le mot-clef « xlambda », mais – c’est la chose importante à
voir – on associe à cette représentation l’environnement courant (la variable env de l’évaluateur), qui sera utilisé
par la suite pour l’évaluation de la lambda expression. L’autre transformation du code est le remplacement de l’appel
à « apply » par cette expression :
(else (xapply-4
(eval-4 (car exp) env)
(map (lambda (x) (eval-4 x env)) (cdr exp))))))
La procédure xapply-4 est elle-même définie ainsi : le cas de xlambda est reconnu spécifiquement, et se traduit
par l’appel de l’évaluateur, appel dans lequel on introduit les liaisons (variables locales) de la lambda-expression en
tête de l’environnement :
(define (xapply-4 fct liste)
(cond
((procedure ? fct) (apply fct liste))
((and (pair ? fct) (eq ? xlambda (car fct)))
(eval-4 (caddr fct)
(append (map cons (cadr fct) liste) (cadddr fct))))
(else (error "xapply error : " (cons fct liste)))))
On dispose dès lors d’un modèle complet et opérationnel de l’évaluateur, comme le montre la session récapitulative
suivante :
(load "evaluator-4.scm")
(scheme-4) ; to run the evaluator
;= #t
(scheme-4)
: (+ 23 31)
=> 54
: (if #t 2 5)
=> 2
: ’toto
=> toto
: (define toto 5)
=> toto
: toto
=> 5
: (set ! toto 8)
=> 8
: toto
=> 8
: (define titi (lambda (x) (+ x 2)))
=> titi
: (titi toto)
=> 10
: (define fact (lambda (x)
(if (<= x 0) 1 (* x (fact (- x 1))))))
=> fact
: (fact 8)
=> 40320
: (define adder (lambda (x)
(lambda (y) (+ x y))))
=> adder
: (define add7 (adder 7))
=> add7
: (add7 2)
=> 9
36 CHAPITRE 2. IMPLANTATION D’UN LANGAGE FONCTIONNEL : SCHEME
2.6 Conclusion
Le développement par étapes et raffinements successifs d’un modèle de l’évaluateur permet de comprendre la
simplicité des concepts mis en oeuvre. Le travail décrit ici est suffisant pour l’écriture d’une première version de
l’interprète dans un langage compilé, version qui peut ensuite être complété par l’ajout de nouvelles primitives,
par l’optimisation des structures de données utilisées pour la représentation des objets du système, etc. Le modèle
présenté fait naturellement l’impasse sur bien des aspects du langage, comme la gestion de la mémoire, le traitement
des continuations, etc. Il est cependant suffisant comme point de départ pour bien appréhender les aspects de scheme.
Bibliographie
[1] Harold Abelson, Gerald Jay Sussman, and Julie Sussman. Structure and Interpretation of Computer Programs.
MIT Press, Cambridge, Mass., 1985.
[2] Harold Abelson, Gerald Jay Sussman, and Julie Sussman. Structure et Interprétation des Programmes Infor-
matiques. InterEditions, Paris, France, 1989.
[3] Harold Abelson, Gerald Jay Sussman, and Julie Sussman. Structure and Interpretation of Computer Programs.
http://mitpress.mit.edu/sicp/, 2001.
[4] Gull Agha. Actors : A Model of Concurrent Computation in Distributed Systems. MIT Press, Cambridge,
Mass., 1986.
[5] Gull Agha and Carl Hewitt. Actors : a conceptual Foundation for Concurrent Object-Oriented Programming.
In B. Shriver and P. Wegner, editors, Research Directions in Object-Oriented Programming. MIT Press, 1987.
[6] H.P. Barendregt. The Lambda Calculus, Its Syntax and Semantics. North Holland, Amsterdam, 1984.
[7] Alonzo Church. The Calculi of Lambda Conversion. Princeton University Press, Princeton, N.J., 1941.
[8] R.Kent Dybvig. The Scheme Programming Language. Prentice-Hall, INC., Englewood Cliffs, New Jersey,
1987.
[9] Daniel P. Friedman, Mitchell Wand, and Christopher T. Haynes. Essentials of Programming Languages. MIT
Press, Cambridge, MA, 1989.
[10] Jean-Jacques Girardot. Les Langages et Les Systèmes Lisp. EDITest, 1985.
[11] Jean-Jacques Girardot. gLisp : Manuel de Référence. Technical report, Ecole des Mines, Saint-Etienne,
Octobre 1990.
[12] M. Gondran. Introduction aux systèmes experts. Bulletin de la Direction des Etudes et Recherches d’E.D.F.,
Série C, Mathématiques Informatiques, (2), 1983.
[13] Chris Hanson. MIT Scheme Reference Manual. Technical report, Massachusetts Institute of Technology,
Boston, Mass., 1991.
[14] Chris Hanson. MIT Scheme User’s Manual. Technical report, Massachusetts Institute of Technology, Boston,
Mass., 1991.
[15] David H.Bartley, John C.Jensen, and Donald W.Oxley. PC Scheme User’s Guide and Language Reference
Manual. the MIT Press, Cambridge, Massachussets, 1988.
[16] J. W. Backus, F. L. Bauer, J. McCarthy, P. Naur, A. J. Perlis, and others... Modifier Report on the Algorithmic
Language Algol 60. Technical report, 1975.
[17] Jean-Jacques Girardot. Initiation à la programmation fonctionnelle par Scheme. http://kiwi.emse.
fr/Misc/bookintro.ps.gz, Septembre 1995.
[18] Jean-Jacques Girardot. Misc User’s Manual. Technical report, École des Mines, Saint-Étienne, France, 2001.
[19] Jean-Jacques Girardot. Sch User’s Manual. Technical report, École des Mines, Saint-Étienne, France, 2003.
[20] J-L. Krivine. Lambda calcul : types et modèles. Masson, 1990.
[21] John Kunz editor. Common Lisp, The Reference. Addison Wesley, Reading, MA, 1988.
[22] Henry Lieberman. Reversible Object-Oriented Interpreters. In ECCOP’87 proceedings, volume 276, pages
13–21, Paris, France, June 1987. Spinger Verlag.
[23] John McCarthy. Lisp 1.5 Programmer’s Manual. Technical report, M.I.T., Cambridge, Mass., 1962.
[24] R4RS. Revised 4 Report on the Algorithmic Language Scheme. ACM Sigplan Notices, 21(12), Décembre
1990.
[25] R5RS. Revised 5 Report on the Algorithmic Language Scheme. Technical report, 1998.
37
38 BIBLIOGRAPHIE
[26] John D. Ramsdell. An Operational Semantics for Scheme. Lisp Pointers, V(2), Avril 1992.
[27] Régis Girard. Programmation Fonctionnelle en Scheme. http://www.univ-reunion.fr/
~rgirard/Enseignements/SchemeCours/cours.html.
[28] George Springer and Daniel P. Friedman. Scheme and the Art of Programming. MIT Press, Cambridge, MA,
1992.
[29] Guy L. Steele Jr. Common Lisp, The language, 2nd edition. Digital Press, Digital Equipment Corporation,
1990.
[30] Warren Teitlman. Interlisp Reference Manual. Technical report, Xerox, Palo Alto, California, 1981.
Index
39
40 INDEX
even ?, 16 char-lower-case ?, 18
exit, 27 char-upper-case ?, 18
exp, 16 char ?, 12, 17
force, 26 environment ?, 13
integer ?, 14 input-port ?, 24
isinf ?, 17 list ?, 21
isnan ?, 17 null ?, 12, 20
length, 21 number ?, 12
list, 21 output-port ?, 25
log, 16 pair ?, 12, 19
max, 16 port ?, 13, 24
member, 23 procedure ?, 13
memq, 23 string ?, 13
memv, 23 symbol ?, 12
min, 16 vector ?, 12
modulo, 15 prédicats, 11
neg, 16 procédure, 11
negative ?, 16 Procédures
not, 13 exit, 6
number ?, 12, 14 procedure ? (prédicat), 13
odd ?, 16 promesse, 9
open-input-string, 25 promise, 9
open-output-string, 25
positive ?, 16 quasiquote, 7
rand, 27 quote (forme spéciale), 7, 11
random-seed, 27
read, 26 rand (opération), 27
read-char, 26 random-seed (opération), 27
remainder, 15 read, 31
reverse, 21 read (opération), 26
reverse!, 21 read-char, 8
set-car!, 19 read-char (opération), 26
set-cdr!, 19 remainder (opération), 15
sin, 16 reverse, 8
sqrt, 16 reverse (opération), 21
tan, 16 reverse! (opération), 21
zero ?, 16
open-input-string (opération), 25 s-exp, 31
open-output-string (opération), 25 sémantique opérationnelle, 4
or (forme spéciale), 7, 10 séparateurs, 7
orthogonal, 3 sch, 3
output-port ? (prédicat), 25 Scheme, 3, 6
set, 8
pair ? (prédicat), 12, 19 set-car, 8
paire, 11 set-car! (opération), 19
paire (définition), 18 set-cdr, 8
paire pointée (définition), 18 set-cdr! (opération), 19
parenthèse, 5 set! (forme spéciale), 7, 11
point, 5 sin (opération), 16
point-virgule, 5 sqrt (opération), 16
port, 26 stderr, 24
port d’entrée-sortie, 11 stdin, 24
port ? (prédicat), 13, 24 stdout, 24
ports, 24 string->list, 8
positive ?, 8 string-length, 8
positive ? (prédicat), 16 string ? (prédicat), 13
Prédicats structures de contrôle, 7
boolean ?, 12 symbol ? (prédicat), 12
char-alphabetic ?, 18 symbole, 11
42 INDEX
symbolic expression, 31
tan (opération), 16
Tri, 27
unquote, 7
unquote-splicing, 7
vecteur, 11
vector ? (prédicat), 12
vrai, 11
write, 31
zero ?, 8
zero ? (prédicat), 16
Table des matières
43
44 TABLE DES MATIÈRES
Bibliographie 37
Index 39