c 2006 Marc Feeley IFT2030 page 181
Parseur fonctionnel (1)
•
Un parseur (ou “analyseur syntaxique”) est unprogramme qui permet de déterminer si une phrase P fait partie d’un langage L ou pas
•
Parseur = fonction qui prends une liste de caractères en entrée et qui retourne un Booléen (#t = la liste de caractères est une phrase qui fait partie du langage, sinon #f)•
Un parseur va examiner les caractères de gauche à droite•
Un stream c’est une liste des caractères qui n’ont pas encore été examinés par le parseur, doncparseur = stream → Booléen
c 2006 Marc Feeley IFT2030 page 182
Parseur fonctionnel (2)
•
Exemple simple: L = { a, b0, b1, ..., b9 }(define L
(lambda (stream)
(or (and (not (null? stream))
(char=? #\a (car stream)) (null? (cdr stream)))
(and (not (null? stream))
(char=? #\b (car stream)) (not (null? (cdr stream)))
(char-numeric? (cadr stream)) (null? (cddr stream))))))
(L ’(#\a)) => #t (L ’(#\a #\5)) => #f
•
On aimerait une spécification de plus haut niveau du langage basé sur une grammairec 2006 Marc Feeley IFT2030 page 183
Parseur fonctionnel (3)
•
Idée: représenter chaque catégorie de la grammaire par une fonctionparseur = stream → (stream | #f)
Si le stream en entrée contient un fragment de phrase qui correspond à la catégorie, le reste de la phrase est retourné, sinon #f
c 2006 Marc Feeley IFT2030 page 184
Parseur fonctionnel (4)
•
Les catégories <a> et <b>:(define a ; r`egle: <a> ::= "a"
(lambda (stream)
(and (not (null? stream))
(char=? #\a (car stream)) (cdr stream))))
(define b ; r`egle: <b> ::= "b"
(lambda (stream)
(and (not (null? stream))
(char=? #\b (car stream)) (cdr stream))))
(a ’(#\a)) => ()
(a ’(#\a #\b #\c)) => (#\b #\c) (a ’(#\b #\a)) => #f
(b ’(#\b #\a)) => (#\a)
c 2006 Marc Feeley IFT2030 page 185
Parseur fonctionnel (5)
(define c ; r`egle: <c> ::= "0" |...| "9"
(lambda (stream)
(and (not (null? stream))
(char-numeric? (car stream)) (cdr stream))))
(define bc ; r`egle: <bc> ::= <b> <c>
(lambda (stream)
(let ((reste (b stream))) (and reste
(c reste)))))
(define abc ; r`egle: <abc> ::= <a> | <bc>
(lambda (stream) (or (a stream)
(bc stream)))) (define L
(lambda (stream)
(null? (abc stream)))) (L ’(#\b #\4 #\a)) => #f
c 2006 Marc Feeley IFT2030 page 186
Parseur fonctionnel (6)
•
Amélioration: faire abstraction des patrons•
Constructeur de parseur = fonction qui retourne un parseur(define char
(lambda (test)
(lambda (stream)
(and (not (null? stream)) (test (car stream)) (cdr stream)))))
(define char=
(lambda (c)
(char (lambda (x) (char=? x c))))) (define a (char= #\a))
(define b (char= #\b))
(define c (char char-numeric?))
c 2006 Marc Feeley IFT2030 page 187
Parseur fonctionnel (7)
(define seq
(lambda (p1 p2)
(lambda (stream)
(let ((reste (p1 stream))) (and reste
(p2 reste)))))) (define choix
(lambda (p1 p2)
(lambda (stream) (or (p1 stream)
(p2 stream))))) (define fin null?)
(define bc (seq b c))
(define abc (choix a bc)) (define L (seq abc fin))
(L ’(#\b #\4)) = ((seq abc fin) ’(#\b #\4))
= ... = #t
c 2006 Marc Feeley IFT2030 page 188
Parseur fonctionnel (8)
•
La spécification du langage est maintenant très proche de la grammaire:; librairie de construction de parseur:
(define char (lambda (test) ...)) (define char= (lambda (c) ...))
(define seq (lambda (p1 p2) ...)) (define choix (lambda (p1 p2) ...)) (define fin null?)
; sp´ecification du langage L:
(define L
(seq (choix (char= #\a)
(seq (char= #\b)
(char char-numeric?))) fin))
c 2006 Marc Feeley IFT2030 page 189
Parseur fonctionnel (9)
•
Extension pour exprimer l’itération?(define vide ; fragment vide (lambda (stream)
stream))
(define iter+ ; it´eration non-nulle (lambda (p)
(seq p (iter* p))))
(define iter* ; it´eration possiblement nulle (lambda (p)
(lambda (stream)
((choix (iter+ p) vide) stream))))
(define L
(seq (iter+ (char char-numeric?)) (seq (char= #\.)
(seq (iter* (char char-numeric?)) fin))))
c 2006 Marc Feeley IFT2030 page 190
Parseur fonctionnel (10)
•
Construction automatique de l’ASA•
Idée: changer la signature des parseursparseur = stream → ((ASA × stream) | #f)
c’est-à-dire qu’un parseur retourne soit #f ou bien une paire qui contient l’ASA correspondant au fragment de phrase examiné par le parseur et le reste de la phrase
(define res cons) ; constructeur (define res-ASA car) ; partie ASA
(define res-stream cdr) ; partie stream
c 2006 Marc Feeley IFT2030 page 191
Parseur fonctionnel (11)
(define char
(lambda (test)
(lambda (stream)
(and (not (null? stream)) (test (car stream))
(res (car stream) (cdr stream)))))) (define seq
(lambda (p1 p2)
(lambda (stream)
(let ((r1 (p1 stream))) (and r1
(let ((r2 (p2 (res-stream r1)))) (and r2
(res (cons (res-ASA r1) (res-ASA r2))
(res-stream r2))))))))) (define vide (lambda (stream) (res ’() stream))) (define fin (lambda (stream)
(and (null? stream)
(res ’() stream))))
c 2006 Marc Feeley IFT2030 page 192
Parseur fonctionnel (12)
(define L
(seq (iter+ (char char-numeric?)) (seq (char= #\.)
(seq (iter* (char char-numeric?)) fin))))
(res-ASA (L ’(#\1 #\2 #\. #\3 #\4)))
=> ((#\1 #\2) #\. (#\3 #\4))
car cdr
car cdr
()
car cdr
#\2
car cdr
()
car cdr car cdr
()
car cdr
#\.
#\1
#\3
#\4
c 2006 Marc Feeley IFT2030 page 193
Continuations (1)
•
Déf: la continuation d’une fonction f c’est une fonction k passée en paramètre à f qui indique quel calcul il faut faire avec “le(s) résultat(s)” de la fonction f•
Exemple: fonction qui additionne 2 nombres(define add
(lambda (x y cont) (cont (+ x y))))
(add 2 3 (lambda (r) (* r r))) => 25
c 2006 Marc Feeley IFT2030 page 194
Continuations (2)
•
Exemple pratique: “retourner” plusieurs résultats(define racines ; racines de axˆ2+bx+c=0 (lambda (a b c cont)
(let ((d (sqrt (- (* b b) (* 4 a c))))) (cont (/ (+ (- b) d) (* 2 a))
(/ (- (- b) d) (* 2 a))))))
(racines 1 0 -1 (lambda (r1 r2) (* r1 r2)))
=> -1
•
Dans cet exemple l’utilisation d’une continuation évite la création d’une liste (pour retourner les 2 racines) etc’est plus élégant que:
(let ((r (racines 1 0 -1))) ; version ``liste’’
(let ((r1 (car r)) (r2 (cadr r))) (* r1 r2)))
c 2006 Marc Feeley IFT2030 page 195
Continuations (3)
•
Exemple: partitionner une liste en 2 suivant unecertaine propriété des éléments (fonction qui retourne un Booléen), il y a 2 résultats:
1. éléments pour lesquels la fonction donne #t 2. éléments pour lesquels la fonction donne #f
(define partitionner (lambda (f lst cont)
(if (null? lst)
(cont ’() ’()) (partitionner
f
(cdr lst)
(lambda (a b)
(if (f (car lst))
(cont (cons (car lst) a) b)
(cont a (cons (car lst) b))))))))
c 2006 Marc Feeley IFT2030 page 196
Continuations (4)
(define trier ; implantation de ``Quicksort’’
(lambda (f lst) (if (null? lst)
’()
(let ((p (car lst))) (partitionner
(lambda (x) (f x p)) (cdr lst)
(lambda (a b)
(append (trier f a)
(cons p (trier f b))))))))) (trier < ’(5 8 2 3 1 9)) => (1 2 3 5 8 9)
c 2006 Marc Feeley IFT2030 page 197
Continuations (5)
•
À l’aide des continuations, il est possible de transformer n’importe quelle fonction en une fonction en formeitérative
•
L’idée c’est d’ajouter une continuation à toute fonction qui est utilisée dans un appel non-terminal, afin detransformer cet appel en un appel terminal (la
continuation représente le calcul qui consomme le résultat de l’appel)
c 2006 Marc Feeley IFT2030 page 198
Continuations (6)
•
Exemple simple: calcul de la factorielle(define fact (lambda (n)
(if (= n 0) 1
(* n (fact (- n 1))))))
; transformation en forme it´erative:
(define fact-fi
(lambda (n cont) (if (= n 0)
(cont 1)
(fact-fi (- n 1)
(lambda (r)
(cont (* n r))))))) (define fact
(lambda (n)
(fact-fi n (lambda (r) r))))
c 2006 Marc Feeley IFT2030 page 199
Continuations (7)
> (define fact-fi (lambda (n cont)
(if (= n 0) (cont 1)
(fact-fi (- n 1)
(lambda (r)
(cont (* n r)))))))
> (define fact (lambda (n) (fact-fi n (lambda (r) r))))
> (trace fact fact-fi)
> (fact 5)
| > (fact 5)
| > (fact-fi 5 ’#<procedure #2>)
| > (fact-fi 4 ’#<procedure #3>)
| > (fact-fi 3 ’#<procedure #4>)
| > (fact-fi 2 ’#<procedure #5>)
| > (fact-fi 1 ’#<procedure #6>)
| > (fact-fi 0 ’#<procedure #7>)
| 120 120
; Avec:
; #<procedure #2> = (lambda (r) r)
; #<procedure #3> = (lambda (r) (’#<procedure #2> (* 5 r)))
; #<procedure #4> = (lambda (r) (’#<procedure #3> (* 4 r)))
; #<procedure #5> = (lambda (r) (’#<procedure #4> (* 3 r)))
; #<procedure #6> = (lambda (r) (’#<procedure #5> (* 2 r)))
; #<procedure #7> = (lambda (r) (’#<procedure #6> (* 1 r)))
Les fermetures créées pour la continuation de fact-fi jouent le même rôle que la pile d’exécution
c 2006 Marc Feeley IFT2030 page 200
Continuations (8)
•
Autre exemple: append(define append
(lambda (lst1 lst2) (if (null? lst1)
lst2
(cons (car lst1)
(append (cdr lst1) lst2)))))
; transformation en forme it´erative:
(define append-fi
(lambda (lst1 lst2 cont) (if (null? lst1)
(cont lst2)
(append-fi (cdr lst1) lst2
(lambda (r)
(cont (cons (car lst1) r))))))) (define append
(lambda (lst1 lst2)
(append-fi lst1 lst2 (lambda (r) r))))
c 2006 Marc Feeley IFT2030 page 201
Continuations (9)
> (trace append append-fi)
> (append ’(1 2 3) ’(4 5))
| > (append ’(1 2 3) ’(4 5))
| > (append-fi ’(1 2 3) ’(4 5) ’#<procedure #2>)
| > (append-fi ’(2 3) ’(4 5) ’#<procedure #3>)
| > (append-fi ’(3) ’(4 5) ’#<procedure #4>)
| > (append-fi ’() ’(4 5) ’#<procedure #5>)
| (1 2 3 4 5) (1 2 3 4 5)
; Avec:
; #<procedure #2> = (lambda (r) r)
; #<procedure #3> = (lambda (r) (’#<procedure #2> (cons 1 r)))
; #<procedure #4> = (lambda (r) (’#<procedure #3> (cons 2 r)))
; #<procedure #5> = (lambda (r) (’#<procedure #4> (cons 3 r)))
c 2006 Marc Feeley IFT2030 page 202
Continuations (10)
•
Les continuations sont aussi utiles pour les algorithmes génériques de fouille•
Un arbre binaire peut être représenté à l’aide de listes:noeud = (val gauche droite) et arbre vide = ()
Donc (4 (3 () ()) (-1 (-8 () ()) ())) représente 4
3 -1
-8
c 2006 Marc Feeley IFT2030 page 203
Continuations (11)
•
Voici une fonction de fouille par parcours infixe de l’arbre(define fouille-infixe
(lambda (arbre visite fin) ; 2 continuations (if (null? arbre)
(fin)
(fouille-infixe (cadr arbre) visite
(lambda ()
(visite (car arbre) (lambda ()
(fouille-infixe (caddr arbre) visite
fin))))))))
c 2006 Marc Feeley IFT2030 page 204
Continuations (12)
•
Utilisation 1: trouver le premier nombre négatif(fouille-infixe
’(4 (3 () ()) (-1 (-8 () ()) ())) (lambda (x cont)
(if (< x 0) x (cont))) (lambda ()
#f))
=> -8
4
3 -1
-8
c 2006 Marc Feeley IFT2030 page 205
Continuations (13)
•
Utilisation 2: convertir l’arbre en liste(fouille-infixe
’(4 (3 () ()) (-1 (-8 () ()) ())) (lambda (x cont)
(cons x (cont))) (lambda ()
’()))
=> (3 4 -8 -1)
4
3 -1
-8
c 2006 Marc Feeley IFT2030 page 206
Continuations (14)
•
Utilisation 3: liste des nombres impairs(fouille-infixe
’(4 (3 () ()) (-1 (-8 () ()) ())) (lambda (x cont)
(if (odd? x) (cons x (cont)) (cont))) (lambda ()
’()))
=> (3 -1)
4
3 -1
-8
c 2006 Marc Feeley IFT2030 page 207
Tests de type (1)
•
Il existe des fonctions primitives pour testerdynamiquement le type d’une donnée (ces fonctions sont applicables sur n’importe quel type de donnée et retournent #f ou #t)
(null? x) => x est la liste vide?
(pair? x) => x est une liste non-vide?
(char? x) => x est un caractère?
(number? x) => x est un nombre?
(string? x) => x est une chaîne de caractères?
(symbol? x) => x est un symbole?
(boolean? x) => x est un Booléen (#f ou #t)?
(procedure? x) => x est une procédure?
c 2006 Marc Feeley IFT2030 page 208
Tests de type (2)
•
Exemple(define f
(lambda (nom)
(string-append "allo "
(if (symbol? nom)
(symbol->string nom) nom))))
(f ’marc) => "allo marc"
(f "marc") => "allo marc"
c 2006 Marc Feeley IFT2030 page 209
Affectation et effets de bord (1)
•
Scheme n’est pas un langage purement fonctionnel car il possède l’affectation (et des fonctionsd’entrée/sortie)
•
Le style impératif est donc possible•
Afin de traiter l’affectation correctement chaque variable dans l’environnement est en fait une cellule dont lecontenu peut être modifié par affectation
•
L’affectation se fait avec la forme spéciale set!:• (set! hvari hexpri) évalue hexpri et stocke le résultat dans la cellule de la variable hvari
• set! retourne une valeur indéfinie
c 2006 Marc Feeley IFT2030 page 210
Affectation et effets de bord (2)
•
Exemple> (define x 1)
> (set! x (+ x 1))
> x 2
> (set! x "allo")
> x
"allo"
c 2006 Marc Feeley IFT2030 page 211
Affectation et effets de bord (3)
•
Scheme possède également la forme spéciale begin qui exprime un séquencement d’évaluations (utile pour forcer l’ordre d’exécution des effets de bord)• (begin hexpr1i ... hexprNi) évalue chaque sous-expression de gauche à droite
• la valeur de hexprNi est retournée
c 2006 Marc Feeley IFT2030 page 212
Affectation et effets de bord (4)
•
Exemple> (begin
(display "hello world!") (newline)
(+ 2 3)) hello world!
5
> (define boucle (lambda ()
(begin
(display "entrer n: ") (let ((x (read)))
(if (number? x) (begin
(write (sqrt x)) (newline)
(boucle)))))))
> (boucle) entrer n: 10
3.1622776601683795 entrer n: 4
2
entrer n: fin
c 2006 Marc Feeley IFT2030 page 213
Affectation et effets de bord (5)
•
Il y a une interaction interessante entre les fermetures et les affectations•
Chaque fermeture mémorise l’environnement de la lambda-expression correspondante incluant les cellules associées:> (define accumuler (let ((n 0))
(lambda (x)
(begin (set! n (+ n x)) n))))
> (accumuler 5) 5
> (accumuler 5) 10
•
Dans cet exemple, on a encapsulé la variable nc 2006 Marc Feeley IFT2030 page 214
Encapsulation
•
Déf: une variable ou fonction est encapsulée si elle est accessible seulement aux parties du programme quiont une raison légitime d’y accéder
•
L’encapsulation permet d’accroître la modularité (car dépendances claires) et robustesse (on peut garantir des invariants par examination locale du code: parexemple, n toujours un nombre)
•
L’encapsulation est une approche utile enprogrammation orientée-objet (les données sont encapsulées dans la définition de classe)
c 2006 Marc Feeley IFT2030 page 215
Encapsulation: Exemple
1. (define creer-compte
2. (lambda (solde)
3. (lambda (msg)
4. (cond ((equal? msg ’depot)
5. (lambda (n)
6. (if (< n 0)
7. (error "d´epot erron´e")
8. (set! solde (+ solde n)))))
9. ((equal? msg ’retrait)
10. (lambda (n)
11. (if (or (< n 0) (> n solde))
12. (error "retrait erron´e")
13. (set! solde (- solde n)))))
14. ((equal? msg ’lire-solde)
15. (lambda () solde))
16. (else
17. (error "op´eration erron´ee"))))))
18.
19. (define c1 (creer-compte 100))
20.
21. (define c2 (creer-compte 0))
22.
23. ((c1 ’retrait) 30)
24.
25. ((c2 ’depot) 15)
26.
27. ((c1 ’lire-solde)) => 70
28. ((c2 ’lire-solde)) => 15
29.
30. ((c1 ’retrait) 80) => *** ERROR -- retrait erron´e
c 2006 Marc Feeley IFT2030 page 216
Encapsulation et Modules (1)
•
L’encapsulation sert aussi à créer des modules qui cachent leur implantation des clients•
En Scheme: affectation + définitions locales•
Exemple: module de génération de nombrespseudo-aléatoires suivant certaines distributions
• fonction exportée (loi-uniforme a b)
• fonction exportée (loi-exponentielle m)
• fonction interne (entier-aleatoire)
c 2006 Marc Feeley IFT2030 page 217
Encapsulation et Modules (2)
1. (define loi-uniforme #f)
2. (define loi-exponentielle #f)
3.
4. (let () ; fronti`ere d’encapsulation
5.
6. (define graine 1673) ; ´etat du g´en´erateur
7. (define k1 3581) ; constantes du g´en´erateur congruentiel
8. (define k2 12751)
9. (define k3 131072)
10.
11. (define entier-aleatoire
12. (lambda ()
13. (begin
14. (set! graine (modulo (+ (* graine k1) k2) k3))
15. graine)))
16.
17. (define uniforme
18. (lambda (a b) ; a et b sont les bornes de l’intervalle
19. (+ a (* (- b a) (/ (entier-aleatoire) (- k3 1))))))
20.
21. (define exponentielle
22. (lambda (m) ; nombre entre 0 et +inf, m est la moyenne
23. (- (* m (log (uniforme 0 1))))))
24.
25. (begin
26. (set! loi-uniforme uniforme)
27. (set! loi-exponentielle exponentielle)))
c 2006 Marc Feeley IFT2030 page 218
Mutation des Données (1)
•
Scheme offre aussi des fonctions de mutation pour modifier le contenu des données structurées• paires: (set-car! p d) et (set-cdr! p d)
• chaînes: (string-set! s i c)
• vecteurs: (vector-set! v i d)
c 2006 Marc Feeley IFT2030 page 219
Mutation des Données (2)
•
Exemple: renverser une liste destructivement(define reverse!
(lambda (lst)
(define rev! ; d´efinition ``locale’’
(lambda (lst res) (if (null? lst)
res
(let ((temp (cdr lst))) (begin
(set-cdr! lst res) (rev! temp lst)))))) (rev! lst ’())))
(define x (list 3 4 5))
(define y (cons 1 (cons 2 x))) (reverse! y) => (5 4 3 2 1) x => (3 2 1)
y => (1)