Prog rammation F onctionnelle Introduction au cours "Prog

publicité
Programmation Fonctionnelle
SI4 – 2010-2011
Erick Gallesio eg(@)unice.fr
Une introduction
Programmation Fonctionnelle
Erick Gallesio eg(@)unice.fr ()
SI4 – 2010-2011
SI4 – 2010-2011
Introduction au cours "Programmation Fonctionnelle" (1)
Pourquoi un cours de programmation fonctionnelle?
Pourquoi Scheme?
Programmation Fonctionnelle
Lisp
Scheme est un dialecte Lisp
petit langage
très utilisé dans l’enseignement
nombreux supports de cours (livres, Web)
nombreuses implémentations (Unix, Windows)
norme de fait (R 6 RS) + SRFI
R 7 RS en cours de finalisation
Erick Gallesio eg(@)unice.fr ()
1 / 22
2 / 22
SI4 – 2010-2011
Introduction au cours "Programmation Fonctionnelle" (2)
Scheme est un vieux langage, mais il est très utilisé
regain d’intérêt depuis quelques années
petit donc facilement "embarquable"
Langage de "glue"
Guile pour les projets de la FSF
Programmation Fonctionnelle
Applications utilisant un Scheme/Lisp embarqué
Emacs
AutoCAD
GIMP
Gnumeric
...
Erick Gallesio eg(@)unice.fr ()
Introduction au cours "Programmation Fonctionnelle" (3)
Dicton poluplaire:
Those who do not understand Unix are condemned to reinvent it, poorly.
– Henry Spencer
La même chose est vraie pour Lisp (moi :-)
Java 1.0 (1997): Syntaxe C++ mais un GC + ...
Java 1.1 (1998): inner classes + ...
Java 1.2 (1998): Reflection, Collections + ...
Java 1.3 (2000): Java Platform Debugger Architecture + ...
Java 1.4 (2002): integrated XML parser and XSLT processor + ...
Java 5.0 (2004): for-each loop, generics, autoboxing and var-args + ...
SI4 – 2010-2011
3 / 22
4 / 22
Java 6.0 (2006): facilite l’utilisation de langages de scripts (javascript) + ...
Java 7.0 (2011?): amélioration diverses (plus les lambda)
Programmation Fonctionnelle
Java 8.0 (????): lambda expressions (informally, "closures")
Erick Gallesio eg(@)unice.fr ()
Systèmes Scheme
Programmation Fonctionnelle
En général, consulter le site http://www.schemers.org
Implémentations
Dr Scheme
Guile
MIT Scheme
SCM
Implémentations niçoises:
Bigloo
STk
STklos
Erick Gallesio eg(@)unice.fr ()
Documents, Cours
SI4 – 2010-2011
5 / 22
6 / 22
De nombreux documents sont disponibles sur le Web pour apprendre Scheme:
Scheme in Fixnum Days de Dorai Sitaram
SI4 – 2010-2011
http://www.ccs.neu.edu/home/dorai/t-y-scheme/t-y-scheme.html
The Scheme Programming Language de Kent Dybvig
http://www.scheme.com/tspl2d/index.html
Programmation Fonctionnelle
How to Design Programs (PLT) http://www.htdp.org
Erick Gallesio eg(@)unice.fr ()
Introduction
C’est quoi?
Lambda Calcul (Alonzo Church 1940)
LISP (John Mc Carthy 1960)
LISP 1.5 1960
MacLisp
Machines Lisp (Zeta Lisp)
Common Lisp
Programmation Fonctionnelle
Rudiments de Scheme
Programmation Fonctionnelle
Scheme (Sussman & Steele 1975)
R n RS
SRFI
Erick Gallesio eg(@)unice.fr ()
Erick Gallesio eg(@)unice.fr ()
SI4 – 2010-2011
SI4 – 2010-2011
8 / 22
7 / 22
Types de base: nombres (2)
(exact->inexact 10/3)
(/ 10 3)
→3.333333333333
→10/3
SI4 – 2010-2011
#t (pour vrai)
#f (pour faux)
SI4 – 2010-2011
En Scheme, seule la valeur #f est fausse; toute autre valeur est
considérée comme vraie.
Chaînes de caractères
Taille quelconque
Programmation Fonctionnelle
12 / 22
11 / 22
7505999378950827/2251799813685248
→
(make-polar 2 12)
Erick Gallesio eg(@)unice.fr ()
Programmation Fonctionnelle
(make-rectangular 3 2)
2+3i
Complexes
(inexact->exact (/ 10 3.0)
10/3
Les rationnels sont "exacts"
Syntaxe
9 / 22
Rationnels
SI4 – 2010-2011
La syntaxe est simple
→(+ 2 3)
→(+ 2 3 x y)
→(+ 2 (* 3 x))
notation parenthésée
écriture préfixe
2+3
2+3+x+y
2+3*x
Programmation Fonctionnelle
Types de base: Booléens et Chaînes de caractères
Erick Gallesio eg(@)unice.fr ()
Types de base: nombres (1)
Booléens
→255
Deux valeurs possibles:
Entiers
10
#xff
→15
1000
#o17
On peut aussi avoir des "bignums" (précision “infinie”)
Rééls
1.23
Erick Gallesio eg(@)unice.fr ()
"Une chaine"
10 / 22
3.2e58
SI4 – 2010-2011
"Regles classiques:\"guillemets\" et \n"
Programmation Fonctionnelle
Les rééls sont "inexacts"
Erick Gallesio eg(@)unice.fr ()
Définition de variables (1)
Erick Gallesio eg(@)unice.fr ()
Programmation Fonctionnelle
> (define x 10)
x
> (define y (+ x 1))
y
> x
10
> y
11
> (+ x y)
21
Types de base: Symboles (1)
13 / 22
La forme define permet de définir une association nom-valeur.
SI4 – 2010-2011
En gros, tout ce qui ne peut pas être pris pour autre chose (en fait c’est un peu
plus compliqué). On ne fait pas la différence entre minuscules et majuscules
Exemples
toto
ToTo
+
*
exemple-1
un-symbole-tres-tres-long
exact->inexact
Programmation Fonctionnelle
Définition de variables (2)
Erick Gallesio eg(@)unice.fr ()
Types de base: Symboles (2)
Erick Gallesio eg(@)unice.fr ()
Programmation Fonctionnelle
SI4 – 2010-2011
SI4 – 2010-2011
> (define add +)
; add est maintenant définie. Utilisons la.
> (add 1 2)
3
Essayons maintenant d’utiliser ces symboles
Super!!!
> +
#<subr +> ;la fct primitive pour additionner
> *
#<subr *> ;la fct primitive pour multiplier
Que se passe t’il pour les symboles pré-definis
14 / 22
Expressions symboliques
SI4 – 2010-2011
Données manipulées par l’interprète: expressions symboliques (sexpr)
Mécanisme d’évaluation:
Programmation Fonctionnelle
(- (+ 2 (* 3 5)) 7)
→ (- (+ 2 15) 7)
→ (- 17 7)
→ 10
Erick Gallesio eg(@)unice.fr ()
15 / 22
16 / 22
Fonctions (1)
Les fonctions sont donc des objets comme les autres en Scheme.
On dit qu’elles sont de première classe.
On peut définir des fonctions utilisateur avec la forme spéciale lambda
> (lambda(x) (* x 2))
#<closure>
; on peut aussi utiliser la notation suivante
> (λ(x) (* x 2))
#<closure>
SI4 – 2010-2011
Mais on ne peut pas faire grand chose avec cette fonction → on utilise
donc define
Programmation Fonctionnelle
> (define double (λ(x) (* x 2)))
; double est maintenant définie
> (double 12)
24
Erick Gallesio eg(@)unice.fr ()
Fonctions (2)
On peut avoir des définitions à l’intérieur d’une fonction
Programmation Fonctionnelle
SI4 – 2010-2011
(define hypotenuse
(λ(a b)
; hypotenuse est une fonction à 2 paramètres
(define carre (λ(x) (* x x)))
√
; Résultat: a2 + b2
(sqrt (+ (carre a)
(carre b)))))
Erick Gallesio eg(@)unice.fr ()
17 / 22
18 / 22
Structures de contrôle: if (1)
Forme spéciale if
(if <test>
<action1>)
ou bien
(if <test>
<action1>
<action2>)
Exemple:
Programmation Fonctionnelle
> (define a 1)
> (define b 2)
> (if (< a b) a b)
1
Erick Gallesio eg(@)unice.fr ()
Structures de contrôle: if (2)
Exemple 1:
> (if (+ 1 2) "vrai" "faux")
"vrai"
Exemple 2:
Programmation Fonctionnelle
(define signe
(λ(n)
(if (< n 0)
-1
(if (> n 0)
1
0))))
Erick Gallesio eg(@)unice.fr ()
SI4 – 2010-2011
SI4 – 2010-2011
20 / 22
19 / 22
Structures de contrôle: cond
(cond
(clause1 action1)
(clause2 action2)
. . .
(else action))
évaluation en séquence
la clause else n’est pas obligatoire
Programmation Fonctionnelle
(define signe
(λ(n)
(cond
((< n 0) -1)
((> n 0) 1)
(else
0))))
Erick Gallesio eg(@)unice.fr ()
Structures de contrôle: begin
La forme spéciale begin
permet de regrouper une série d’expressions
a pour valeur, la valeur de la dernière expression
(begin
expr1
expr2
...
exprn)
Programmation Fonctionnelle
Cette forme est souvent utile dans un if
(if (< a b)
(begin
(foo a)
(bar b)))
Erick Gallesio eg(@)unice.fr ()
SI4 – 2010-2011
SI4 – 2010-2011
22 / 22
21 / 22
Programmation Fonctionnelle
SI4 – 2010-2011
Erick Gallesio eg(@)unice.fr
Les listes
Programmation Fonctionnelle
Erick Gallesio eg(@)unice.fr ()
Notation
(vert orange rouge)
Une liste se note entre parenthèses
Une liste peut contenir des listes
((Nom Dupond) (Prenom Jean) (Naissance (jour
10) (mois 10) (annee 1910)))
ou encore
((Nom Dupond)
(Prenom Jean)
(Naissance (jour 10)
(mois 10)
(annee 1910)))
Programmation Fonctionnelle
Cette forme a été obtenue avec un "pretty-printer"
Erick Gallesio eg(@)unice.fr ()
SI4 – 2010-2011
SI4 – 2010-2011
1 / 25
2 / 25
Construction de liste
La primitive de base pour construire des listes: list
Cette primitive construit une liste
SI4 – 2010-2011
SI4 – 2010-2011
4 / 25
3 / 25
de longueur égale au nombre de ses paramètres
dont chaque élément est le résultat de l’évaluation du paramètre
correspondant
> (list 1 2 3 4 5)
(1 2 3 4 5)
> (list 1 2 (+ 2 2) (* 3 4))
(1 2 4 12)
MAIS ...
Programmation Fonctionnelle
> (list un deux trois quatre cinq)
*** Error:
unbound variable: un
Erick Gallesio eg(@)unice.fr ()
La forme spéciale quote (1)
Quote permet de «bloquer» l’évaluation
Similaire aux guillemets en français:
un)
deux)
trois)
quatre)
cinq))
quatre cinq)
Entrez votre nom: _________________
Entrez «votre nom»: ________________
> (list (quote
(quote
(quote
(quote
(quote
(un deux trois
ou encore
Programmation Fonctionnelle
> (quote (un deux trois quatre cinq))
(un deux trois quatre cinq)
Erick Gallesio eg(@)unice.fr ()
La forme spéciale quote (2)
La primitive append
Erick Gallesio eg(@)unice.fr ()
Programmation Fonctionnelle
SI4 – 2010-2011
SI4 – 2010-2011
8 / 25
7 / 25
;ou (append ’(x y z) ’())
Programmation Fonctionnelle
> (append ’() ’(x y z))
(x y z)
> (append ’(a b c) ’() ’(x y z))
(a b c x y z)
> (append ’(a b c) ’(x) ’(y z))
(a b c x y z)
> (append ’(a b c) ’(x y z))
(a b c x y z)
> (append ’(a b c) ’() ’(d))
(a b c d)
append «concatène» des listes pour n’en faire plus qu’une seule:
> (quote un)
un
> ’un
un
SI4 – 2010-2011
5 / 25
Pour simplifier l’écriture de listes constantes le lecteur Scheme propose une
notation spéciale pour la forme quote
Donc:
> (list ’un ’deux ’trois ’quatre ’cinq)
(un deux trois quatre cinq)
> ’(un deux trois quate cinq)
(un deux trois quate cinq)
Programmation Fonctionnelle
> (list ’un ’deux ’trois (+ 2 2) ’cinq)
(un deux trois 4 cinq)
Erick Gallesio eg(@)unice.fr ()
La primitive reverse
Erick Gallesio eg(@)unice.fr ()
> (define lst ’(1 2 3))
> (reverse lst)
(3 2 1)
> lst
(1 2 3)
> (reverse ’(a (b c) (d e f) g) )
(g (d e f) (b c) a)
> (define ab ’(a b))
> (reverse (append ab ab))
(b a b a)
> (reverse ’())
()
> (reverse ’(a b c))
(c b a)
Et pour finir sur list ...
6 / 25
reverse renverse une liste au premier niveau:
SI4 – 2010-2011
Afin d’être complet sur list, voyons le cas particulier suivant
> (list)
()
> ’()
()
Programmation Fonctionnelle
C’est la liste vide que l’on appelle parfois nil.
Erick Gallesio eg(@)unice.fr ()
La primitive length
length calcule le nombre d’éléments dans une liste.
> (define ab ’(a b))
> (length ab)
2
> (length (append ab ab))
4
> (length (list ab ab))
2
> (list ab ab)
((a b) (a b))
Programmation Fonctionnelle
> (length ’((a b c d e f g)))
1
Erick Gallesio eg(@)unice.fr ()
Les prédicats null? et list?
null? permet de savoir si une liste est vide.
> (null? ’() )
#t
> (null? ’(1 2 3) )
#f
Programmation Fonctionnelle
SI4 – 2010-2011
SI4 – 2010-2011
list? permet de savoir si un objet est une liste bien formée.
> (list? ’(1 2 3))
#t
> (list? (list))
#t
> (list? 1)
#f
Erick Gallesio eg(@)unice.fr ()
9 / 25
10 / 25
Les paires
La liste est une structure importante en Scheme.
Ce n’est pas une structure primitive.
Programmation Fonctionnelle
La structure primitive est la paire.
> (cons 1 2)
(1 . 2)
> (cons ’a ’ b)
(a . b)
Erick Gallesio eg(@)unice.fr ()
Primitives sur les paires
Premier élement d’une paire: car
> (define x (cons 1 2))
x
> (car x)
1
Second élement d’une paire: cdr
> (cdr x)
2
Programmation Fonctionnelle
Prédicat de test: pair?
> (pair? (cons 1 2))
#t
> (pair? 12)
#f
> (pair? ’())
#f
Erick Gallesio eg(@)unice.fr ()
SI4 – 2010-2011
SI4 – 2010-2011
12 / 25
11 / 25
Paires et listes (1)
> (define x (cons ’a ’b))
Programmation Fonctionnelle
;; ==> y = (a)
> (define x (cons ’a ’()))
> (car x)
a
> (cdr x)
()
> (define y (list ’a))
> (car y)
a
> (cdr y)
()
Erick Gallesio eg(@)unice.fr ()
Paires et listes (2)
Programmation Fonctionnelle
(define L ’(a (b (c) d) 10)
Les listes sont représentées à l’aide de paires. Ainsi,
est représentée par
Erick Gallesio eg(@)unice.fr ()
SI4 – 2010-2011
SI4 – 2010-2011
13 / 25
14 / 25
Paires et listes (3)
Expérimentons:
Programmation Fonctionnelle
> (cons 1 (cons 2 ’() ))
(1 2)
> (cons 1 (cons 2 3))
(1 2 . 3)
> (cons 1 (cons 2 (cons 3 4))))
(1 2 3 . 4)
Erick Gallesio eg(@)unice.fr ()
Paires et listes (4)
> (define L (list ’A ’B))
> (define LL (cons ’x L))
Programmation Fonctionnelle
On voit que les éléments de listes sont partagés.
Erick Gallesio eg(@)unice.fr ()
SI4 – 2010-2011
SI4 – 2010-2011
16 / 25
15 / 25
Paires et listes (5)
Programmation Fonctionnelle
> (define LLL (append LL L))
Erick Gallesio eg(@)unice.fr ()
Paires et listes (6)
Programmation Fonctionnelle
> (define l1 ’(a b))
> (define l2 ’(1 2 3))
> (define l3 ’((x y)))
Erick Gallesio eg(@)unice.fr ()
SI4 – 2010-2011
SI4 – 2010-2011
17 / 25
18 / 25
Paires et listes (7)
L’expression (list l1 l2 l3) produit 3 nouvelles paires
Programmation Fonctionnelle
⇒ ( (a b) (1 2 3) ((x y)) )
Erick Gallesio eg(@)unice.fr ()
Paires et listes (8)
Programmation Fonctionnelle
L’expression (append l1 l2 l3) produit 5 nouvelles paires
⇒ (a b 1 2 3 (x y))
Erick Gallesio eg(@)unice.fr ()
SI4 – 2010-2011
SI4 – 2010-2011
20 / 25
19 / 25
Égalité de listes
> (define l1 (list 1 2 3))
> (define l2 (list 1 2 3))
Ces listes sont elles égales?
SI4 – 2010-2011
OUI car elles ont le même contenu
NON car elles sont situées à des endroits différents en mémoire
On a donc deux prédicats en Scheme
Programmation Fonctionnelle
equal? pour l’égalité structurelle
eq?pour l’égalité de pointeurs
Erick Gallesio eg(@)unice.fr ()
list? et length
(define list?
(λ()
(cond
((null? l) #t)
((pair? l) (list? (cdr l)))
(else
#f)))
Programmation Fonctionnelle
SI4 – 2010-2011
(define length
(λ(l)
(cond
((null? l) 0)
((pair? l) (+ 1 (length (cdr l))))
(else
(error "length: bad list")))))
Erick Gallesio eg(@)unice.fr ()
21 / 25
22 / 25
append
Version n’acceptant que deux paramètres seulement:
Programmation Fonctionnelle
SI4 – 2010-2011
(define append
(λ(l1 l2)
(cond
((null? l1) l2)
((pair? l1) (cons (car l1)
(append (cdr l1) l2)))
(error "append: bad list")))))
(else
Erick Gallesio eg(@)unice.fr ()
reverse (version 1)
Programmation Fonctionnelle
SI4 – 2010-2011
(define reverse1
(λ(l)
(cond
((null? l) l)
((pair? l) (append (reverse1 (cdr l))
(list (car l))))
(error "reverse1: bad list")))))
(else
Cette version est Θ(n2 )
Erick Gallesio eg(@)unice.fr ()
23 / 25
24 / 25
reverse (version 2)
(define reverse2
(λ(l)
(define aux (λ(l res) ...)
(aux l ’())))
où aux est définie comme
Programmation Fonctionnelle
SI4 – 2010-2011
(λ(l res)
(cond
((null? l) res)
((pair? l) (aux (cdr l) (cons (car l) res)))
(else
(error "reverse2: bad list")))))
Cette version est Θ(n)
Erick Gallesio eg(@)unice.fr ()
25 / 25
Programmation Fonctionnelle
SI4 – 2010-2011
Erick Gallesio eg(@)unice.fr
Fonctions
Programmation Fonctionnelle
Erick Gallesio eg(@)unice.fr ()
SI4 – 2010-2011
1 / 23
200 3 )
Passage de paramètres / Règles de visibilité
Lors de l’appel précédent, on avait
( (λ (a b) (+ a b 1))
Quand on est dans la fonction, on a les associations suivantes:
f =⇒ (λ (a b) (+ a b 1))
x =⇒ 100
—————————————————
a =⇒ 200
b =⇒ 3
Programmation Fonctionnelle
SI4 – 2010-2011
SI4 – 2010-2011
4 / 23
3 / 23
Notes
Lorsqu’on sort de la fonction, les associations à «a» et «b»
“disparaissent”
On sait donc associer de façon temporaire une valeur à un symbole.
Dans la fonction elle même, la variable «f» est visible:
Programmation Fonctionnelle
on peut utiliser des variables globales depuis une fonction
on peut définir des fonctions récursives
Erick Gallesio eg(@)unice.fr ()
Association symbole/valeur
Erick Gallesio eg(@)unice.fr ()
mais ce n’est pas très facile à lire.
( (λ (delta)
(cond
((< delta 0) ...)
((= delta 0) ...)
(else
...)))
(- (* b b) (* 4 a c)))
En Scheme:
Soit delta = b2 − 4ac
si delta < 0 → ...
si delta = 0 → ...
sinon
→ ...
Appel de fonction
2 / 23
En Maths:
SI4 – 2010-2011
Soit la fonction suivante
(define f (λ (a b) (+ a b 1)))
Regardons un appel à cette fonction
(define x 100)
(f (* 2 x) 3)
Lors de l’évaluation «f» et «x» sont substituées par leur valeur:
( (λ (a b) (+ a b 1))
(* 2 100) 3 )
↓
200
3 )
Programmation Fonctionnelle
( (λ (a b) (+ a b 1))
=> 204
Erick Gallesio eg(@)unice.fr ()
La forme spéciale let (1)
Cette forme permet l’association symbole/valeur:
(let ((v1 expr1)
(v2 expr2)
...
(vn exprn))
;; Ici v1,v2 et vn existent
......)
Exemple
Programmation Fonctionnelle
(let ((delta (- (* b b) (* 4 a c))))
(cond
((< delta 0) ...)
((= delta 0) ...)
(else
...)))
à comparer avec:
Soit delta = b2 − 4ac
si delta < 0 → ...
si delta = 0 → ...
sinon
→ ...
Erick Gallesio eg(@)unice.fr ()
La forme spéciale let (2)
La forme générale
(let ((v1 expr1)
(v2 expr2)
...
(vn exprn))
;; Ici v1,v2 et vn existent
......)
est donc en fait équivalente à l’appel de fonction:
Programmation Fonctionnelle
( (λ (v1 v2 ... vn) .....)
expr1
expr2
...
n)
Erick Gallesio eg(@)unice.fr ()
SI4 – 2010-2011
SI4 – 2010-2011
5 / 23
6 / 23
La forme spéciale let (3)
Soit le let suivant:
(let ((x 1)
(y (+ x 1)))
(+ x y))
==> **** Error: reference to undefined symbol x
Récrivons cette expression et voyons pourquoi il y a erreur:
( (λ (x y) (+ x y))
1
(+ x 1))
Le λ − calcul nous apprend que cet appel pourrait aussi s’écrire:
( (λ (vanille fraise) (+ vanille fraise))
1
(+ x 1))
Programmation Fonctionnelle
Ici, il est clair que «x» n’est pas défini
Erick Gallesio eg(@)unice.fr ()
La forme spéciale let (4)
Le code précédent doit en fait être écrit:
(let ((x 1))
(let ((y (+ x 1)))
(+ x y)))
ce qui donne
( (λ (x)
(let ((y (+ x 1)))
(+ x y)))
1)
soit encore
Programmation Fonctionnelle
( (λ (x)
( (λ (y) (+ x y))
(+ x y)))
1)
Erick Gallesio eg(@)unice.fr ()
SI4 – 2010-2011
SI4 – 2010-2011
8 / 23
7 / 23
Un autre problème
Malheureusement, ça ne marche pas !!!!
car la forme précédente est équivalente à
((λ (fact) (fact 1000))
(λ (n) ..... (fact (- n 1))))
Soit encore
Programmation Fonctionnelle
((λ (bidule) (bidule 1000))
(λ (n) ..... (fact (- n 1))))
Erick Gallesio eg(@)unice.fr ()
La forme spéciale letrec
Le let précédent doit s’écrire
(letrec ((fact (λ (n) .... (fact (- n 1)))))
(fact 1000))
Exemple
Programmation Fonctionnelle
(letrec ((pair?
(λ (x)
(if (= x 0)
#t
(impair? (- x 1)))))
(impair? (λ (x)
(if (= x 0)
#f
(pair? (- x 1))))))
(pair? 999))
Erick Gallesio eg(@)unice.fr ()
SI4 – 2010-2011
Cette forme doit être utilisée lorsqu’on veut définir une fonction récursive.
SI4 – 2010-2011
(let ((fact (λ (n) .... (fact (- n 1)))))
(fact 1000))
On pourrait écrire par exemple:
La forme spéciale let*
9 / 23
La forme spéciale let* permet de résoudre le problème précédent
SI4 – 2010-2011
SI4 – 2010-2011
10 / 23
Essayons de calculer 1000! sans définir (“consommer”) de symbole global
«fact».
Forme générale
(let* ((v1 expr1)
(v2 expr2)
...
(vn exprn))
......)
Cette forme est en fait équivalente à:
(let ((v1 expr1))
(let ((v2 expr2))
.
.
.
Programmation Fonctionnelle
(let ((vn exprn))
...)))
Erick Gallesio eg(@)unice.fr ()
Comparaison let / let*
Dans la forme let les expressions sont évaluées en parallèle
−→ elles ne peuvent pas être interdépendantes
Programmation Fonctionnelle
Dans la forme let* les expressions sont évaluées séquentiellement
−→ une expression peut réutiliser un symbole défini auparavant
Erick Gallesio eg(@)unice.fr ()
11 / 23
12 / 23
Les fonctions à arité variable (1)
SI4 – 2010-2011
13 / 23
Scheme permet de définir des fonctions à nombre variable de paramètres
Utilisation de paires pointées dans la lambda
> (define f (λ (a b . c)
(print a b c)))
> (f 1 2)
1 2 ()
Programmation Fonctionnelle
> (f 1)
*** Error: too few arguments
> (f 1 2 3)
1 2 (3)
> (f 1 2 3 4 5 6)
1 2 (3 4 5 6)
Erick Gallesio eg(@)unice.fr ()
Règles de réécriture
Il existe une forme simplifiée pour déclarer les fonctions
(define f (λ (a b c) ... ))
peut s’écrire
(define (f a b c) ....)
(define g (λ a b . c) ... ))
devient
(define (g a b . c) ... )
(define h (λ l ....))
devient
Programmation Fonctionnelle
(define (h . l) ...)
Erick Gallesio eg(@)unice.fr ()
SI4 – 2010-2011
La fonction apply
> (f 1 2 3 4)
(1 2 3 4)
> (g 1 2 3 4)
1 2 3 4
Erick Gallesio eg(@)unice.fr ()
Programmation Fonctionnelle
SI4 – 2010-2011
> (print 1 2 3 4) ;print sera définie plus loin
1 2 3 4
> (define (f . l) (print l))
> (define (g . l) (apply print l))
> (apply + ’(1 2 3))
6
Les fonctions à arité variable (2)
14 / 23
La fonction apply permet d’appliquer une fonction à une liste de valeurs
SI4 – 2010-2011
Si on a aucun paramètre fixe, la liste de paramètres se dénote sans
parenthèses
> (define f (λ lst (print lst)))
> (f)
()
> (f 1)
(1)
> (f 1 2 3)
(1 2 3)
Ça rappelle une fonction pré-définie ...
−→ la fonction list de Scheme
Programmation Fonctionnelle
(define list (λ lst lst))
Erick Gallesio eg(@)unice.fr ()
15 / 23
16 / 23
La fonction map (1)
La fonction for-each
18 / 23
Programmation Fonctionnelle
(define (print . l)
(for-each (λ (x)(display x) (display " "))
l)
;; Passer à la ligne
(display "\n"))
Erick Gallesio eg(@)unice.fr ()
Encore des listes
==> (and (pred l1) ... (pred ln))
==> (or (pred l1) ... (pred ln))
(define (every f l)
(if (null? l)
#t
(and (f (car l))
(every f (cdr l)))))
Programmation Fonctionnelle
(define (some f l)
(if (null? l)
#f
(or (f (car l))
(some f (cdr l)))))
Erick Gallesio eg(@)unice.fr ()
SI4 – 2010-2011
19 / 23
20 / 23
Malheureusement, on ne peut pas appliquer «and» et «or» sur une liste (c’est
de la syntaxe)
(every pred l)
(some pred l)
SI4 – 2010-2011
Différence: cette fonction ne collecte pas les résultats intermédiaires dans
une liste
Cette fonction est similaire à map.
SI4 – 2010-2011
SI4 – 2010-2011
17 / 23
La fonction map permet d’appliquer une fonction à chaque élément d’une liste
−→ Le résultat est la liste des résultats de chaque application.
> (map integer? ’(1 toto "Salut" "123" 100))
(#t #f #f #f #t)
> (map (λ (x) (* x x)) ’(1 2 3 4 5))
(1 4 9 16 25)
Programmation Fonctionnelle
> (define R (map cons ’(a b c) ’(1 2 3)))
R
; R vaut ((a . 1) (b . 2) (c . 3))
Pour retrouver les symboles
> (map car R)
Pour retrouver les valeurs
> (map cdr R)
Erick Gallesio eg(@)unice.fr ()
La fonction map (2)
On représente des vecteurs à l’aide de listes:
(define v1 ’(1 2 3 4 5))
(define v2 ’(6 7 8 9 10))
Pour calculer le produit scalaire «v1.v2» il faut:
calculer 1*6, 2*7, ..., 5*10
> (map * v1 v2)
(6 14 24 36 50)
> (apply + ’(6 14 24 36 50))
130
calculer la somme des produits partiels
Donc,
Programmation Fonctionnelle
> (define (produit-scalaire v1 v2)
(apply + (map * v1 v2)))
> (produit-scalaire v1 v2)
130
Erick Gallesio eg(@)unice.fr ()
)
Erick Gallesio eg(@)unice.fr ()
Programmation Fonctionnelle
( ((z 30) (w 40))
((y 20))
((x 10))
Tous les symboles globaux
a pour environnement
a pour code (+ x y z n)
Le corps de ce let
(let ((x 10))
(let ((y 20))
(let ((z 30)
(w 40))
(+ x y z w n))))
Notion d’environnement (3)
21 / 23
Notion d’environnement (1)
SI4 – 2010-2011
22 / 23
Soit
SI4 – 2010-2011
Soit la définition suivante:
(define f
(let ((x 100))
(display x)
(λ (n)
(+ x n))))
fonction
let
global
Regardons les associations lors de l’appel «(f 1)»
——————————————
f =⇒ (|λ| (n) (+ x n))
x =⇒ 100
——————————————
n =⇒ 1
Une fonction Scheme, c’est:
le code de la fonction
Programmation Fonctionnelle
un environnement associé
Erick Gallesio eg(@)unice.fr ()
Notion d’environnement (2)
Soit
(define (monnaie->euro taux)
(λ (n)
(/ n taux)))
On peut alors définir:
(define franc->euro (monnaie->euro 6.55957))
(define dollar->euro (monnaie->euro 0.73))
(define livre->euro (monnaie->euro 1.47))
....
Les fonctions ont le même code associé:
(λ (n) (/ n taux))
Programmation Fonctionnelle
(pour franc->euro)
(pour dollar->euro)
Par contre, elles ont capturé un environnement différent:
taux −→ 6.55957
taux −→ 0.73
....
Erick Gallesio eg(@)unice.fr ()
SI4 – 2010-2011
23 / 23
Programmation Fonctionnelle
SI4 – 2010-2011
Erick Gallesio eg(@)unice.fr
Fermetures
Programmation Fonctionnelle
Erick Gallesio eg(@)unice.fr ()
Affectation
SI4 – 2010-2011
1 / 22
On ne peut affecter qu’une variable déclarée au préalable (par un define
ou dans un let).
L’affectation est réalisée par la forme spéciale «set!»
SI4 – 2010-2011
2 / 22
> (define x 10)
;; x est définie. On peut maintenant changer sa valeur
> (set! x 20)
> x
20
ou encore
> (let ((var 1))
(set! var (* 2 var))
var)
2
Programmation Fonctionnelle
On essaiera de limiter l’utilisation de l’affectation aux situations où est elle
strictement nécessaire.
Erick Gallesio eg(@)unice.fr ()
Notion d’environnement (Rappel / 1)
Soit la définition suivante:
(define f
(let ((x 100))
(display x)
(λ (n)
(+ x n))))
fonction
let
global
Regardons les associations lors de l’appel «(f 1)»
f =⇒ (|λ| (n) (+ x n))
——————————————
x =⇒ 100
——————————————
n =⇒ 1
Une fonction Scheme, c’est:
le code de la fonction
Programmation Fonctionnelle
un environnement associé
Erick Gallesio eg(@)unice.fr ()
Notion d’environnement (Rappel / 2)
Soit
(define (monnaie->euro taux)
(λ (n)
(/ n taux)))
On peut alors définir:
(define franc->euro (monnaie->euro 6.55957))
(define dollar->euro (monnaie->euro 0.73))
(define livre->euro (monnaie->euro 1.47))
....
Les fonctions ont le même code associé:
(λ (n) (/ n taux))
Programmation Fonctionnelle
(pour franc->euro)
(pour dollar->euro)
Par contre, elles ont capturé un environnement différent:
taux −→ 6.55957
taux −→ 0.73
....
Erick Gallesio eg(@)unice.fr ()
SI4 – 2010-2011
SI4 – 2010-2011
4 / 22
3 / 22
Environnement et affectation (2)
> (f 1)
3
> (f 1)
4
Erick Gallesio eg(@)unice.fr ()
Programmation Fonctionnelle
A chaque appel, la variable «c» est incrémentée
(define f
(let ((c 1))
(λ(n)
(set! c (+ c 1))
(+ n c))))
Notion d’environnement (Rappel / 3)
5 / 22
Soit la fonction
SI4 – 2010-2011
Soit
(let ((x 10))
(let ((y 20))
(let ((z 30)
(w 40))
(+ x y z w n))))
Le corps de ce let
a pour code (+ x y z n)
a pour environnement
( ((z 30) (w 40))
((y 20))
((x 10))
Tous les symboles globaux
)
Programmation Fonctionnelle
Define et affectation (1)
Erick Gallesio eg(@)unice.fr ()
Environnement et affectation (1)
On peut toujours écrire une définition «en 2 fois»:
(define x 10)
peut se récrire en:
(define x #f)
...
(set! x 10)
Donc, la fonction précédente peut se récrire en
(define f #f)
...
Programmation Fonctionnelle
(set! f (let ((c 1))
(λ(n)
(set! c (+ c 1))
(+ n c))))
Erick Gallesio eg(@)unice.fr ()
SI4 – 2010-2011
SI4 – 2010-2011
;; ou n’importe quelle autre valeur
Soit la fonction suivante
(define f
(let ((c 1))
(λ(n)
(+ n c))))
a la même durée de vie que la fonction «f»
n’est visible (et donc utilisable) que dans la fonction «f»
SI4 – 2010-2011
6 / 22
Ici, «c» joue un rôle similaire à celui d’une variable statique de C ou de Java:
=⇒
Programmation Fonctionnelle
Si on modifie «c» lors d’un appel, la valeur modifiée sera accessible au
prochain appel
Erick Gallesio eg(@)unice.fr ()
7 / 22
8 / 22
Define et affectation (2)
Define et affectation (4)
Erick Gallesio eg(@)unice.fr ()
Programmation Fonctionnelle
SI4 – 2010-2011
SI4 – 2010-2011
12 / 22
11 / 22
Cela correspond à la notion de Type Abstrait de Données (ou ADT)
La variable commune n’est accessible qu’à travers ces deux fonctions
Deux fonctions qui peuvent accéder à une variable commune
Ce qu’on a:
(let ((c 0))
(set! incr (λ() (set! c (+ c 1))))
(set! decr (λ() (set! c (- c 1)))))
(define incr #f)
(define decr #f)
Exemple:
9 / 22
Dans la forme
(define f #f)
...
(set! f (let ((c 1))
(λ(n)
(set! c (+ c 1))
(+ n c))))
le symbole «f» est aussi accessible depuis le corps du let.
SI4 – 2010-2011
=⇒ On peut donc aussi récrire cette fonction sous la forme suivante:
(define f #f)
...
(let ((c 1))
(set! f (λ(n)
(set! c (+ c 1))
(+ n c))))
Programmation Fonctionnelle
ADT Stack (1)
Erick Gallesio eg(@)unice.fr ()
Define et affectation (3)
push
pop
print-stack
empty-stack?
Erick Gallesio eg(@)unice.fr ()
Programmation Fonctionnelle
Les primitives que l’on doit construire:
On veut construire le Type Abstrait de Données Stack
SI4 – 2010-2011
10 / 22
On a vu que:
Une fonction capture les variables d’un let englobant
On peut affecter une fonction à une variable globale
Les variables du let ne sont accessibles que depuis la fonction qui les
capture (notion de variable privée)
Programmation Fonctionnelle
=⇒ on peut avoir deux (ou plusieurs) fonctions qui partagent une (ou
plusieurs) variables privées.
Erick Gallesio eg(@)unice.fr ()
push #f)
pop #f)
print-stack #f)
empty-stack? #f)
Construisons maintenant l’ADT Stack
ADT Stack (2)
(define
(define
(define
(define
(let ((S ’()))
(set! push (λ(v) (set! S (cons v S))))
(set! pop
(λ() (display S)))
(λ()
(if (empty-stack?)
(error "Empty stack")
(let ((top (car S)))
(set! S (cdr S))
top))))
(set! print-stack
Programmation Fonctionnelle
(set! empty-stack? (λ() (null? S))))
Erick Gallesio eg(@)unice.fr ()
ADT Stack (3)
On a bien un ADT
Mais,deux problèmes majeurs avec cette version
Pollution de l’espace de noms. Si on voulait généraliser:
empty-stack?
empty-queue?
empty-deque?
...
Une seule pile pour tout le programme
Programmation Fonctionnelle
Nous allons essayer de résoudre ces deux problèmes.
Erick Gallesio eg(@)unice.fr ()
SI4 – 2010-2011
SI4 – 2010-2011
13 / 22
14 / 22
(λ(v) ...))
(λ() ...))
(λ() ...))
(λ() ...))
Pollution de l’espace de noms (1)
Regardons le code suivant:
(let ((S ’()))
(define push
(define pop
(define print-stack
(define empty-stack?
<<<< ICI >>>>
)
Ce qu’on peut dire:
Les fonctions «push», «pop», ... "voient" la variable S.
Les fonctions «push», «pop», ... ne sont accessibles que dans let
Programmation Fonctionnelle
SI4 – 2010-2011
Du point <<<< ICI >>>>, on voit les fonctions «push», «pop», ...
Erick Gallesio eg(@)unice.fr ()
Pollution de l’espace de noms (2)
En plaçant une fonction au point <<<< ICI >>>>, on peut:
appeler chacune des fonctions internes
et, par conséquent, accéder à la variable interne
On obtient:
Programmation Fonctionnelle
SI4 – 2010-2011
(define aiguiller
(λ(operation . arg)
(case operation
((empiler)
(push (car arg)))
((depiler)
(pop))
((imprimer)
(print-stack))
((vide?)
(empty-stack?))
(else
(error "Operation inconnue: "
operation)))))
Erick Gallesio eg(@)unice.fr ()
15 / 22
16 / 22
(λ(v) ...))
(λ() ...))
(λ() ...))
(λ() ...))
(λ(op . arg) ...)
Pollution de l’espace de noms (3)
Soit le code:
(let ((S ’()))
(define push
(define pop
(define print-stack
(define empty-stack?
(define aiguiller
aiguiller)
La valeur de ce «let» est
la fonction «aiguiller» et donc
Programmation Fonctionnelle
le moyen d’accéder aux fonctions et données internes
Erick Gallesio eg(@)unice.fr ()
Première version de l’ADT Stack (1)
(define pile (let ((S ’()))
(define push ...)
(define pop
...)
(define print-stack ...)
(define empty-stack? ...)
(λ(op . arg)
...)))
’vide?)
’empiler 1)
’empiler 2)
’vide?)
’depiler)
’imprimer)
Programmation Fonctionnelle
SI4 – 2010-2011
SI4 – 2010-2011
17 / 22
18 / 22
La valeur de pile est la fonction d’aiguillage (la dernière expression du «let»).
> (pile
#t
> (pile
> (pile
> (pile
#f
> (pile
2
> (pile
(1)
Erick Gallesio eg(@)unice.fr ()
Code complet:
Première version de l’ADT Stack (2)
Programmation Fonctionnelle
SI4 – 2010-2011
(push (car arg)))
(pop))
(print-stack))
(empty-stack?))
(error "Bad message" msg))))))
(define pile
(let ((S ’()))
(define push
(λ(v) (set! S (cons v S))))
(define pop
(λ() (if (empty-stack?)
(error "Empty stack")
(let ((top (car S)))
(set! S (cdr S))
top))))
(define print-stack (λ() (display S)))
(define empty-stack? (λ() (null? S)))
(λ(msg . arg)
(case msg
((empiler)
((depiler)
((imprimer)
((vide?)
(else
Erick Gallesio eg(@)unice.fr ()
Deuxième version de l’ADT Stack (1)
On a résolu le problème de la pollution de l’espace de noms, mais on a
toujours qu’une seule pile dans le programme.
SI4 – 2010-2011
Il faut définir un «générateur» de pile: à chaque exécution =>
construction d’une nouvelle pile;
(define (make-stack)
(let ((S ’()))
...))
Simple, il suffit d’embarquer le «let» dans une fonction.
On a donc,
Programmation Fonctionnelle
> (define p1 (make-stack))
> (define p2 (make-stack))
> (p1 ’empiler 1)
> (p1 ’vide?)
#f
> (p2 ’vide?)
#t
Erick Gallesio eg(@)unice.fr ()
19 / 22
20 / 22
Code complet:
Deuxième version de l’ADT Stack (2)
Programmation Fonctionnelle
SI4 – 2010-2011
(push (car arg)))
(pop))
(print-stack))
(empty-stack?))
(error "Bad message ~S" msg))))))
(define (make-stack)
(let ((S ’()))
(define push
(λ(v) (set! S (cons v S))))
(define pop
(λ() (if (empty-stack?)
(error "Empty stack")
(let ((top (car S)))
(set! S (cdr S))
top))))
(define print-stack (λ() (display S)))
(define empty-stack? (λ() (null? S)))
(λ(msg . arg)
(case msg
((empiler)
((depiler)
((imprimer)
((vide?)
(else
Erick Gallesio eg(@)unice.fr ()
Des ADTs aux Objets
Avec les ADTs, on a
des données privées
des fonctions privées qui peuvent accéder au données
possibilité d’avoir plusieurs instances de l’ADT
Dans les langages objets à base de classes, une classe a
des données privées
des fonctions privées qui peuvent accéder au données (méthodes)
possibilité d’avoir plusieurs instances de la classe (objets)
possibilité de construire une classe à partir d’une autre (héritage)
Programmation Fonctionnelle
SI4 – 2010-2011
Pour passer des ADTs aux objets, il ne nous reste donc qu’à savoir
implémenter l’héritage.
Erick Gallesio eg(@)unice.fr ()
21 / 22
22 / 22
Programmation Fonctionnelle
SI4 – 2010-2011
Erick Gallesio eg(@)unice.fr
Macros
Programmation Fonctionnelle
Erick Gallesio eg(@)unice.fr ()
SI4 – 2010-2011
1 / 27
Pour simplifier, on suppose qu’il y a toujours 2 expr. dans le corps d’un «when»
Appel de fonction: Évaluation (2)
(define (when cond a1 a2)
(if cond
(begin a1 a2)))
SI4 – 2010-2011
Les appels à display et à fact sont réalisés avant le test de la condition
( (λ(cond a1 a2) (if cond (begin a1 a2)))
(< a 0)
(display "a est negatif")
(fact (- a)))
(when (< a 0)
(display "a est negatif")
(fact (- a)))
Reprenons l’utilisation précédente de cette forme:
=⇒
Donc
Programmation Fonctionnelle
On exécute toujours le corps du when quelle que soit la valeur de «a».
Erick Gallesio eg(@)unice.fr ()
Macros: Mécanisme de base
Donc, on veut que:
soit transformé en (macro-expansion)
(when (< a 0) (display ...) (fact ...))
1
puis évalué
Programmation Fonctionnelle
(si a < 0 bien-sûr)
(if (< a 0) (begin (display ...) (fact ...)))
2
a est negatif
1234
Erick Gallesio eg(@)unice.fr ()
SI4 – 2010-2011
d’évaluer ensuite le code en Scheme «pur» pour obtenir le résultat.
de remplacer le code de l’utilisateur (when ....) par un code équivalent
exprimé en Scheme «pur»: (if ....)
Appel de fonction: Évaluation (1)
2 / 27
On a besoin d’un nouveau mécanisme qui permette
SI4 – 2010-2011
On veut définir une nouvelle forme:
(when (< a 0)
(display "a est negatif")
(fact (- a)))
Cette forme peut être simulée facilement en Scheme «pur»
(if (< a 0)
(begin
(display "a est negatif")
(fact (- a))))
Programmation Fonctionnelle
Essayons de construire «when» à l’aide d’une fonction
Erick Gallesio eg(@)unice.fr ()
3 / 27
4 / 27
Macros: Définition
SI4 – 2010-2011
Une macro peut être définie avec la forme spéciale define-macro. Cette
forme
prend en arguments le code de l’utilisateur (et non pas des valeurs)
doit retourner le code qui doit être évalué sous forme de liste
(define-macro (when cond a1 a2)
(list ’if cond
(list ’begin a1 a2)))
Lors de l’appel
(when (< a 0) (display ...) (fact ...))
on a les associations suivantes dans la macro
cond −→ (< a 0)
a1 −→ (display ...)
a2 −→ (fact ...)
Macro-expansion de cette utilisation de la macro:
Programmation Fonctionnelle
(if (< a 0) (begin (display ...) (fact ...)))
5 / 27
Macro-expansion
La fonction macro-expand
l’exécution du corps n’a lieu que si l’expression est fausse
(define-macro (unless cond . args)
(list ’if (list ’not cond)
(cons ’begin args)))
Cela donne
> (macro-expand ’(unless (> n 100)
(foo 1)
(bar 200)))
Programmation Fonctionnelle
(if (not (> n 100)) (begin (foo 1) (bar 200)))
Erick Gallesio eg(@)unice.fr ()
SI4 – 2010-2011
SI4 – 2010-2011
8 / 27
7 / 27
permet d’obtenir la forme «macro-expansée» d’une utilisation de macro
est très utile pour tester une macro.
> (macro-expand ’(when C (foo 1) (bar 2)))
(if C (begin (foo 1) (bar 200)))
Programmation Fonctionnelle
> (macro-expand ’(when (not (f)) (f (g h))))
(if (not (f)) (begin (f (g h))))
Erick Gallesio eg(@)unice.fr ()
La macro unless (1)
Erick Gallesio eg(@)unice.fr ()
La macro when
La forme «unless»
6 / 27
joue un rôle similaire à la forme «when».
La version précédente de «when» n’admet que 2 paramètres.
SI4 – 2010-2011
Essayons de la généraliser: au lieu de prendre explicitement 2 paramètres, on
les collecte dans une liste
Utilisation de la notation «.» comme pour les fonctions
Programmation Fonctionnelle
(define-macro (when cond . args)
(list ’if cond
(cons ’begin args)))
Erick Gallesio eg(@)unice.fr ()
La macro unless (2)
(define-macro (when cond . args)
(list ’if cond
(cons ’begin args)))
(define-macro (unless cond . args)
(list ’if (list ’not cond)
(cons ’begin args)))
Problèmes:
Les macro-expansions sont semblables mais le code qui les produit est
assez différent.
Beaucoup de quotes
Difficile à lire/écrire
SI4 – 2010-2011
Le mécanisme de «quasiquote» va nous aider pour définir des macros
Programmation Fonctionnelle
9 / 27
Quasiquote (2)
Exemples d’utilisations de quasiquote et de unquote
SI4 – 2010-2011
> (let ((a 100))
‘(liste avec beaucoup de constantes ... a = ,a))
(liste avec beaucoup de constantes ... a = 100)
1)))
Programmation Fonctionnelle
> (let ((n 1))
‘(n = ,n et n+1 = ,(+ n
(n = 1 et n+1 = 2)
Erick Gallesio eg(@)unice.fr ()
Quasiquote (3)
Erick Gallesio eg(@)unice.fr ()
Quasiquote (1)
Programmation Fonctionnelle
unquote-splicing est dénoté par «,@»
Erick Gallesio eg(@)unice.fr ()
SI4 – 2010-2011
11 / 27
12 / 27
on peut utiliser «unquote-splicing» dans une expression quasiquote pour
construire une liste et “oublier” les parenthèses de cette liste
la forme ,args «ramène» des parenthèses en trop
Mais ce code est incorrect!!!
> (macro-expand ’(when (> n 0) (foo 1) (bar 2)))
(if (> n 0) (begin ((foo 1) (bar 2))))
et essayons là
(define-macro (when cond . args)
‘(if ,cond
(begin ,args)))
Réécrivons la macro «when»
SI4 – 2010-2011
10 / 27
Ce mécanisme
indépendant des macros
mais, souvent utilisé pour exprimer le corps d’une macro
permet de construire une liste dont les composants sont (quasi)
systématiquement «quotés»
permet (éventuellement) d’évaluer certains composants de la liste
Conventions
quasiquote se dénote avec le symbole «‘»
Programmation Fonctionnelle
unquote (qui permet d’évaluer) se dénote avec le symbole «,»
Erick Gallesio eg(@)unice.fr ()
Quasiquote (4)
Macros Scheme / Macros C (1)
macro C:
expansée par le pré-processeur
Programmation Fonctionnelle
le code produit est traduit par le compilateur
Erick Gallesio eg(@)unice.fr ()
SI4 – 2010-2011
La macro «when» (version correcte)
13 / 27
Même si les macros de Scheme sont plus puissantes que les macros de C,
leur traitement est assez semblable:
(define-macro (when cond . args)
‘(if ,cond
(begin ,@args)))
> (macro-expand ’(when (> n 0) (foo 1) (bar 2)))
(if (> n 0) (begin (foo 1) (bar 2)))
La macro «unless»
(define-macro (unless cond . args)
‘(if (not ,cond)
(begin ,@args)))
SI4 – 2010-2011
> (macro-expand ’(unless (> n 0) (foo 1) (bar 2)))
(if (not (> n 0)) (begin (foo 1) (bar 2)))
Programmation Fonctionnelle
Macros Scheme / Macros C (2)
Erick Gallesio eg(@)unice.fr ()
Quasiquote (5)
Erick Gallesio eg(@)unice.fr ()
Il sera donc aussi efficace
Programmation Fonctionnelle
SI4 – 2010-2011
(define (foo n) ... (if (< a b) (begin ....) ...)
sera transformé avant compilation/interprétation en
(define (foo n) ... (when (< a b) ...) ...)
Donc, le code
macro-expansion
évaluation (Interprétation / Compilation)
macro Scheme:
SI4 – 2010-2011
14 / 27
Si on compare le code de when et de unless, c’est mieux car:
Programmation Fonctionnelle
Les macro-expansions sont semblables et le code qui les produit l’est
aussi
Les quotes ont disparu
Plus facile à lire/écrire
Erick Gallesio eg(@)unice.fr ()
15 / 27
16 / 27
La macro ++ (1)
La transmission des paramètres se fait par valeur en Scheme. On ne
peut pas écrire une fonction qui incrémente une variable.
> (define (++ var pas)
(set! var (+ var pas)))
> (define v 10)
> (++ v 1)
> v
10
Par contre, avec une macro on peut simuler le code d’une affectation
> (define-macro (++ var pas)
‘(set! ,var (+ ,var ,pas)))
Programmation Fonctionnelle
SI4 – 2010-2011
> (define v 10)
> (macro-expand ’(++ v 1)) ;; Voyons la macro-expansion
(set! v (+ v 1))
> (++ v 1)
> v
11
Erick Gallesio eg(@)unice.fr ()
17 / 27
La macro ++ (3)
SI4 – 2010-2011
On voit sur l’expansion que notre écriture de la macro n’est pas très
judicieuse:
Évaluation systématique de (car ’(20)) qui est constant
Nouvelle écriture
(define-macro (++ var . pas)
(if (null? pas)
‘(set! ,var (+ ,var 1))
‘(set! ,var (+ ,var ,(car pas)))))
Maintenant:
> (macro-expand ’(++ v 20))
(set! v (+ v 20))
au lieu de
Programmation Fonctionnelle
(set! v (+ v (car ’(20))))
Erick Gallesio eg(@)unice.fr ()
(echanger! x y)
,x))
,y)
tmp)))
Hygiène (1)
(define-macro
‘(let ((tmp
(set! ,x
(set! ,y
Essayons de l’utiliser
> (define a 1)
> (define b 2)
> (echanger! a b)
> (list a b)
(2 1)
Programmation Fonctionnelle
> (macro-expand ’(echanger! a b))
(let ((tmp a)) (set! a b) (set! b tmp))
Erick Gallesio eg(@)unice.fr ()
SI4 – 2010-2011
La macro ++ (2)
18 / 27
On désire réaliser la macro «echanger!» permettant d’échanger les valeurs
de 2 variables
SI4 – 2010-2011
Version avec un pas facultatif:
(define-macro (++ var . pas)
(if (null? pas)
‘(set! ,var (+ ,var 1))
‘(set! ,var (+ ,var (car ’,pas)))))
Essayons là:
> (macro-expand ’(++ v))
(set! v (+ v 1))
Programmation Fonctionnelle
> (macro-expand ’(++ v 20))
(set! v (+ v (car ’(20))))
Erick Gallesio eg(@)unice.fr ()
19 / 27
20 / 27
Let (1)
extraire-vars
−→ (map cadr bindings)
−→ (map car bindings)
(define-macro (let bindings . body)
‘( (λ ,(extraire-vars bindings) ,@body)
,@(extraire-valeurs bindings)))
extraire-valeurs
Programmation Fonctionnelle
Programmation Fonctionnelle
(define-macro (let bindings . body)
‘( (λ ,(map car bindings) ,@body)
,@(map cadr bindings)))
Erick Gallesio eg(@)unice.fr ()
SI4 – 2010-2011
SI4 – 2010-2011
ou encore (en introduisant directement les corps de fonctions) =⇒
(define-macro (let bindings . body)
‘( (λ ,(extraire-vars bindings) ,@body)
,@(extraire-valeurs bindings)))
(define (extraire-valeurs bindings)
(map cdr bindings))
(define (extraire-vars bindings)
(map car bindings))
On peut donc écrire
Let (2)
Erick Gallesio eg(@)unice.fr ()
Avec
On peut constuire «let» avec une macro:
( (λ (x y) (cons x y))
1 2)
était équivalent à
(let ((x 1) (y 2)) (cons x y))
Hygiène (2)
étonnant !!!!!
Nous avons vu que
;; c’est
21 / 27
22 / 27
Cette définition de la macro semble correcte. Essayons une autre utilisation
> (define a 1)
> (define tmp 2)
> (echanger! a tmp)
> (list a tmp)
(1 1)
> (macro-expand ’(echanger! a tmp))
(let ((tmp a)) (set! a tmp) (set! tmp tmp))
la macro dépend de son environnement d’utilisation.
SI4 – 2010-2011
echanger! ne peut pas être utilisée avec une variable de nom «tmp»
Programmation Fonctionnelle
On dit que cette macro est non hygiénique.
Erick Gallesio eg(@)unice.fr ()
Hygiène (3)
Scheme (R 5 RS) propose des macros hygiéniques.
Ce système est différent du système présenté ici.
SI4 – 2010-2011
Pour régler les problèmes d’hygiène, on utilise la fonction «gensym»
«gensym» renvoie un symbole qui n’existe (et n’existera) pas
(define-macro (echanger! x y)
(let ((tmp (gensym)))
‘(let ((,tmp ,x))
(set! ,x ,y)
(set! ,y ,tmp))))
Programmation Fonctionnelle
> (macro-expand ’(echanger! a tmp))
(let ((G122 a))
(set! a tmp)
(set! tmp G122))
Erick Gallesio eg(@)unice.fr ()
23 / 27
24 / 27
Letrec (2)
SI4 – 2010-2011
26 / 27
Erick Gallesio eg(@)unice.fr ()
Programmation Fonctionnelle
...))
SI4 – 2010-2011
(define-macro (letrec bindings . body)
‘(let ,(map (λ(x) (list (car x) #f)) bindings)
,@(map (λ(x) ‘(set! ,@x)) bindings)
,@body))
On peut maintenant écrire la macro qui implémente «letrec»
(map (|λ|(x) ‘(set! ,@x)) bindings)
−→ ( (set! pair? ...) (set! impair?
Générer les affectations
(map (|λ|(x) (list (car x) #f)) bindings)
−→ ((pair? #f) (impair? #f))
Let*
25 / 27
Générer les bindings
SI4 – 2010-2011
Let* peut aussi être écrit à l’aide d’une macro:
(define-macro (let* bindings . body)
(if (<= (length bindings) 1)
‘(let ,bindings ,@body)
‘(let (,(car bindings))
(let* ,(cdr bindings)
,@body))))
> (macro-expand ’(let* ((a 1)) a))
(let ((a 1)) a)
1)
(+ a 1))
b))
b c)))
Programmation Fonctionnelle
> (macro-expand ’(let* ((a
(b
(c
(list a
(let ((a 1))
(let* ((b (+ a 1))
(c b))
(list a b c)))
Erick Gallesio eg(@)unice.fr ()
Letrec (1)
Letrec doit permettre la définition de fonctions récursives
(letrec ((pair?
(λ(x) (if (= x 0) #t (impair? (- x 1)))))
(impair? (λ(x) (if (= x 0) #f (pair?
(- x 1))))))
(pair? 999))
peut en fait être réécrit en
(let ((pair? #f)
(impair? #f))
(set! pair?
(λ(x) (if (= x 0) #t (impair? (- x 1)))))
(set! impair? (λ(x) (if (= x 0) #f (pair?
(- x 1)))))
(pair? 999))
Programmation Fonctionnelle
En fait, en R 5 RS, c’est un peu plus compliqué que cela, mais on se contentera
de cette équivalence ici.
Erick Gallesio eg(@)unice.fr ()
27 / 27
Programmation Fonctionnelle
SI4 – 2010-2011
Erick Gallesio eg(@)unice.fr
Langage à objets
Programmation Fonctionnelle
Erick Gallesio eg(@)unice.fr ()
ADT Stack (1)
(λ(v) ...))
(λ() ...))
(λ() (display S)))
(λ() (null? S))))
push #f)
pop #f)
print-stack #f)
empty-stack? #f)
Nous avions défini la première version de l’ADT Stack comme:
(define
(define
(define
(define
(let ((S ’()))
(set! push
(set! pop
(set! print-stack
(set! empty-stack?
Notons que le let peut se réécrire en
(λ(v) ...))
(λ() ...))
(λ() (display S)))
(λ() (null? S))))
Programmation Fonctionnelle
(let ()
(define S ’())
(set! push
(set! pop
(set! print-stack
(set! empty-stack?
Erick Gallesio eg(@)unice.fr ()
SI4 – 2010-2011
SI4 – 2010-2011
1 / 25
2 / 25
ADT Stack (2)
On veut une forme plus simple pour l’utilisateur
On va utiliser une macro
Forme souhaitée:
(define-ADT Stack
;; Primitives exportées
(push pop print-stack empty-stack?)
;; Definitions
(define S ’())
(define push
(λ(v) ...))
(define pop
(λ() ...))
(define print-stack (λ() ...))
(define empty-stack? (λ() ...)))
La macro define-ADT aura la forme:
Programmation Fonctionnelle
(define-macro (define-ADT name exported . body)
‘(begin
...))
Erick Gallesio eg(@)unice.fr ()
ADT Stack (3)
(map (λ(x) ‘(define ,x #f)) exported)
SI4 – 2010-2011
SI4 – 2010-2011
Construction des «define» externes (pour les primitives exportées):
A l’intérieur du let on a:
une affectation si la variable est exportée
un define si la variable n’est pas exportée
Programmation Fonctionnelle
(map (λ(x)
(if ‘‘le symbole est exporte’’
‘‘construire une affectation’’
;; laisser le define tel quel
x))
body)
Erick Gallesio eg(@)unice.fr ()
3 / 25
4 / 25
ADT Stack (4)
Le symbole est exporté:
(member (cadr x) exported)
‘(set!
,@(cdr x))
Construire une affectation:
On peut donc maintenant construire complètement la macro
(define-macro (define-TAD name exported . body)
‘(begin
,@(map (λ(x) ‘(define ,x #f)) exported)
(let ()
,@(map (λ(x)
(if (member (cadr x ) exported )
‘(set ! , @(cdr x ))
x))
body))))
Programmation Fonctionnelle
SI4 – 2010-2011
Remarque: «name» est juste là pour faire "joli". Il n’est pas utilisé.
Erick Gallesio eg(@)unice.fr ()
ADT Stack (5)
Programmation Fonctionnelle
SI4 – 2010-2011
(define-ADT Stack
(push pop print-stack empty-stack?) ; exportations
(define S ’())
(define push
(λ(v) ...))
(define pop
(λ() ...))
(define print-stack (λ() ...))
(define empty-stack? (λ() ...)))
=>
(begin
(define push
#f)
(define pop
#f)
(define print-stack #f)
(define empty-stack? #f)
(let ()
(define S ’())
(set! push
(λ(v) ...))
(set! pop
(λ() ...))
(set! print-stack (λ() ...))
(set! empty-stack? (λ() ...))))
Erick Gallesio eg(@)unice.fr ()
5 / 25
6 / 25
ADT Stack avec aiguillage (1)
’())
Nous avions vu que le type abstrait Stack pouvait s’écrire:
(define (stack)
(let ()
(define S
Programmation Fonctionnelle
SI4 – 2010-2011
(push (car arg)))
(pop))
(print))
(empty?))
(error "Bad message" msg))))))
(define push
(λ(v) (set! S (cons v S))))
(define pop
(λ() (if (empty-stack?)
(error "Empty stack")
...))
(define print (λ() (display S)))
(define empty? (λ() (null? S)))
(λ(msg . arg)
(case msg
((push)
((pop)
((print)
((empty?)
(else
Erick Gallesio eg(@)unice.fr ()
ADT Stack avec aiguillage (2)
(push (car arg)))
(pop))
(print))
(empty?))
(error "Bad message" msg))))
Dans cette version, la fonction d’aiguillage:
(λ(msg . arg)
(case msg
((push)
((pop)
((print)
((empty?)
(else
SI4 – 2010-2011
push arg))
pop arg))
print arg))
empty? arg))
"Bad message" msg))))
Programmation Fonctionnelle
(apply
(apply
(apply
(apply
(error
; code indépendant du nbre de param.
peut être récrite de la façon suivante
(λ(msg . arg)
(case msg
((push)
((pop)
((print)
((empty?)
(else
Erick Gallesio eg(@)unice.fr ()
7 / 25
8 / 25
ADT Stack avec aiguillage (3)
ADT Stack avec aiguillage (5)
Erick Gallesio eg(@)unice.fr ()
SI4 – 2010-2011
type print
Programmation Fonctionnelle
set-y!
type print +
SI4 – 2010-2011
set-y!
Programmation Fonctionnelle
Classe Circle:
slots: x y + radius
méthodes: get-x get-y set-x!
get-radius set-radius!
slots: x y
méthodes: get-x get-y set-x!
Classe Point:
Introduction aux objets (1)
Erick Gallesio eg(@)unice.fr ()
(define-ADT Stack (push pop print-stack empty-stack?)
(define S
’())
(define push (λ(v) ...))
(define pop
(λ() ...))
)
⇒
(define (Stack)
(let ()
(define S
’())
(define push
(λ(v) ...))
(define pop
(λ() ...))
...
; Produire l’aiguillage pour push, pop, print-stack & empty-stack?
(λ(msg . arg)
(case msg
((push)
(apply push arg)
((pop)
(apply pop arg))
...
(else
(error "Bad message" msg)))))))
Donc
9 / 25
Si on dispose de la liste des symboles exportés dans la variable «exported»,
il est facile de construire automatiquement la fonction d’aiguillage:
SI4 – 2010-2011
‘(λ(msg . arg)
(case msg
;; Produire une clause pour chaque symbole exporté
,@(map .... exported)
(else (error "Bad message" msg))))
où le code .... peut être défini comme
Programmation Fonctionnelle
(λ(x)
‘((,x) (apply ,x arg)))
Erick Gallesio eg(@)unice.fr ()
ADT Stack avec aiguillage (4)
La forme externe que l’on avait définie avant peut être réutilisée pour un ADT
avec aiguillage. On avait:
(define-ADT Stack (push pop print-stack empty-stack?)
(define S
’())
(define push (λ(v) ...))
(define pop
(λ() ...))
...)
SI4 – 2010-2011
10 / 25
Finalement, la macro permettant d’obtenir l’implémentation d’un tel ADT est:
Programmation Fonctionnelle
(define-macro (define-ADT name exported . body)
‘(define (,name)
(let ()
,@body ; ⇒laisser le code de l’utilisateur inchangé
‘(λ(msg . arg)
(case msg
,@(map (λ(x)
‘((,x) (apply ,x arg)))
exported)
(else (error "Bad message" msg)))))))
Erick Gallesio eg(@)unice.fr ()
11 / 25
12 / 25
Introduction aux objets (2)
Classe «Point» peut être représentée par un ADT (comme la pile)
Classe «Circle» peut être représentée aussi par un ADT
14 / 25
13 / 25
Mais on préfère construire «Circle» au dessus de «Point» (notion d’héritage)
SI4 – 2010-2011
SI4 – 2010-2011
Certains slots/méthodes sont nouveaux: «get-radius» ou «radius»
Certains slots/méthodes sont hérités: «x» ou «set-y!»
Programmation Fonctionnelle
Certaines méthodes sont surchargées: «type» ou «print»
Erick Gallesio eg(@)unice.fr ()
Introduction aux objets (3)
(define (make-point x0 y0)
(let ((x x0)
(y y0))
(λ(msg . arg)
(case msg
((get-x)
x)
((get-y)
y)
((set-x!) (set! x (car arg)))
((set-y!) (set! x (car arg)))
((type)
’Point)
((print)
(show "Point: x =" x "y =" y))
(else
(error "Message inconnu:" msg))))))
> (define p1 (make-point 1 1))
> (p1 ’print)
Point: x = 1 y = 1
Programmation Fonctionnelle
> (begin (p1 ’set-y! 100) (p1 ’get-y))
100
Erick Gallesio eg(@)unice.fr ()
Introduction aux objets (4)
Pour la classe «Circle»
on construit un représentant de la classe de base (un «point»)
quand le message à traiter est inconnu on délègue à la classe de base
(else)
La classe Circle peut s’écrire
Programmation Fonctionnelle
SI4 – 2010-2011
r)
(set! r (car arg)))
’Circle)
(show "Circle: \{") (super ’print) (show "\}")
(show "radius =" radius))
(apply super msg arg ) )))))
(define (make-circle x0 y0 r0)
(let ( (super (make − point x0 y 0) )
(radius r0))
(λ(msg . arg)
(case msg
((get-radius)
((set-radius!)
((type)
((print)
(else
Erick Gallesio eg(@)unice.fr ()
Introduction aux objets (5)
> (c ’get-x)
1
> (c ’print)
Circle: {Point: x = 1 y = 2} radius = 3
> (define c (make-circle 1 2 3))
Utilisation de la classe «Circle»
Ici,
«super» est le représentant de la super-classe
«print» utilise l’impression de la super classe
Programmation Fonctionnelle
SI4 – 2010-2011
le «else» permet de déléguer le traitement des messages à la
super-classe si on ne sait pas le faire à ce niveau.
Erick Gallesio eg(@)unice.fr ()
15 / 25
16 / 25
Objets: Implémentation (1)
On voudrait pouvoir écrire la définition d’une classe ainsi:
(define-class Point ()
;; Slots
((x 0) (y 0))
;; Méthodes
(define method1 (λ() ...))
(define method2 (λ(a b) ...)))
Ce qui serait «bien»
génération automatique des accesseurs:
get-x, get-y, set-x!, set-y!
type, print, ...
génération automatique de certains messages:
Programmation Fonctionnelle
Note: On ne s’occupe pas de l’héritage pour l’instant
Erick Gallesio eg(@)unice.fr ()
Objets: Implémentation (2)
x)
y)
(set! x (car arg)))
(set! y (car arg)))
(define (point)
(let ((x 0) (y 0))
(define method1 (λ(x)...))
(define method2 (λ(a b c)...))
(λ(msg . arg)
(case msg
((get-x)
((get-y)
((set-x!)
((set-y!)
((type)
((print)
’point)
(show "#<" ’point)
(show ’x "=" x) (show ’y "=" y)
(show ">"))
Programmation Fonctionnelle
((method1) (apply method1 arg))
((method2) (apply method2 arg))
(else
(error "Bad message" msg))))))
Erick Gallesio eg(@)unice.fr ()
SI4 – 2010-2011
SI4 – 2010-2011
17 / 25
18 / 25
Objets: Implémentation (3)
La macro suivante permet d’implémenter la forme define-class
(define-macro (define-class name superclass bindings . body)
(let ((slots (map car bindings)))
‘(define (,name)
(let ,bindings
,@body
(λ(msg . arg)
(case msg
;; Les accesseurs
,@(map make-reader slots)
,@(map make-writer slots)
;; Les messages type et print
((type) ’,name)
((print) ,@(make-print name slots))
;; Les methodes
,@(map make-call-method body)
(else (error "Bad message" msg))))))))
Programmation Fonctionnelle
Note: On ne se sert pas ici du paramètre «superclass»
Erick Gallesio eg(@)unice.fr ()
Objets: Implémentation (4)
make-call-method
(define (make-call-method defmethod)
(let ((name (cadr defmethod)))
‘((,name) (apply ,name arg))))
make-print
(define (make-print name slots)
‘((show "#<" ’,name)
,@(map (λ(x) ‘(show ’,x "=" ,x)) slots)
(show ">")))
make-reader
(define (make-reader slot)
‘((,(symbol-append ’get- slot)) ,slot))
make-writer
Programmation Fonctionnelle
(define (make-writer slot)
‘((,(symbol-append ’set- slot ’!))
(set! ,slot (car arg))))
Erick Gallesio eg(@)unice.fr ()
SI4 – 2010-2011
SI4 – 2010-2011
20 / 25
19 / 25
Héritage: Implémentation (3)
((method3)
(else
Erick Gallesio eg(@)unice.fr ()
(define (make-print name superclass slots)
‘((show "#<" ’,name)
,(if (null? superclass)
’()
‘(super ’print))
,@(map (λ(x) ‘(show ’,x "=" ,x)) slots)
(show ">")))
Pour circle on obtient
Programmation Fonctionnelle
( (show "#<" ’circle)
(super ’print)
(show ’radius "=" radius)
(show ">") )
Erick Gallesio eg(@)unice.fr ()
SI4 – 2010-2011
SI4 – 2010-2011
on réécrit la fontion «make-print» (ajout du paramètre superclass)
impression des slots directs seulement (pas d’héritage)
Le code d’impression n’est pas correct
Héritage: Implémentation (4)
Programmation Fonctionnelle
(λ(msg . arg)
(case msg
((get-radius) radius)
((set-radius!) (set! radius (car arg)))
((type)
’circle)
((print)
(show "#<" ’circle)
(show ’radius "=" radius)
(show ">"))
(apply method3 arg))
(apply super msg arg))))))
(define (circle)
(let ((super (point))
(radius 0))
(define method3 (λ(a b c)...))
est
(define-class circle (point)
((radius 0))
(define method3 (λ(a b c) ...)))
Héritage: Implémentation (1)
21 / 25
Avec cette macro, le code produit pour la forme
SI4 – 2010-2011
SI4 – 2010-2011
22 / 25
On désire implémenter l’héritage
On va utiliser la technique de délégation vue précédemment
La classe Circle pourrait s’écrire
(define-class circle (point)
((radius 0))
(define method3 (λ(x y) ...)))
Ce qu’il faut ajouter à la macro précédente
Définition de la variable «super»
C’est facile, il suffit d’ajouter (super (point)) dans le let local
Programmation Fonctionnelle
Ne plus afficher une erreur (dans le else), mais utiliser la délégation
Remplacer (error ...) par (apply super msg arg)
Erick Gallesio eg(@)unice.fr ()
Héritage: Implémentation (2)
(define-macro (define-class name superclass bindings . body)
(let ((slots (map car bindings)))
‘(define (,name)
(let , (cons ‘(super , superclass) bindings) ;; <== ICI
,@body
(λ(msg . arg)
(case msg
;; Les accesseurs
,@(map make-reader slots)
,@(map make-writer slots)
;; Les messages type et print
((type) ’,name)
((print) ,@(make-print name slots))
;; Les methodes
,@(map make-call-method body)
(else
Programmation Fonctionnelle
;; <== ET LÀ
, (if (null ? superclass)
‘(error "Bad message" msg)
‘(apply super msg arg ) ))))))))
Erick Gallesio eg(@)unice.fr ()
23 / 25
24 / 25
Notes
Fonctions non standard utilisées ici:
(define (show . args)
(for-each (λ(x) (display x) (display " ")) args))
Programmation Fonctionnelle
(define (symbol-append . args)
(string->symbol (apply string-append
(map symbol->string args))))
Erick Gallesio eg(@)unice.fr ()
SI4 – 2010-2011
25 / 25
Téléchargement