Planche : 1
Planche : 2
Les paires pointées
Les listes sont représentées par des paires pointées suivant le
schéma:
Rep[listeNonVide] =
( Rep[car(listeNonVide)]
. Rep[cdr(listeNonVide)]
)
Rep[atome] = atome
D'où le nom de paire pointée et le choix du nom pour
pair?.
Est atomique tout ce qui n'est pas une liste non vide.
Planche : 3
Vision liste/arbre binaire/S-expression
Soit la liste ((e d)(c b a) z):
(define (find leaf tree)
(or (equal? leaf tree)
(and (pair? tree)
(or (find leaf (car tree))
(find leaf (cdr tree)) ) ) ) )
(define (look term list)
(if (pair? list)
(or (if (pair? (car list))
(look term (car list))
(equal? term (car list)) )
(look term (cdr list)) )
#f ) )
Planche : 4
Modificateurs physiques
Les paires pointées sont des structures de données muables.
Les « fonctions » set-car! et set-cdr! permettent de
modifier les champs d'un doublet.
(let ((unePaire (cons 'a 'b)))
(set-car! unePaire 'z)
unePaire )
=> (z . b)
On peut donc créer des structures de données cycliques comme,
par exemple, la liste infinie (0 1 0 1 0 ...):
(let ((c (list 0 1)))
(set-cdr! (cdr c) c)
c )
Attention à ne pas imprimer! En DrScheme, cela donne,
comme en Common Lisp, #0=(0 1 . #0#).
Planche : 5
Égalité
Trois sortes d'égalité:
-
equal?
- teste l'égalité structurelle de deux valeurs.
- eq?
- teste l'égalité physique de deux valeurs.
- eqv?
- teste l'égalité physique ou numérique de deux valeurs.
(equal? (cons 'a 'b) (cons 'a 'b)) ;
(eq? (cons 'a 'b) (cons 'a 'b)) ;
(let ((x (cons 'a 'b)))
(eq? x x) ) ;
(let ((x (cons 'a 'b)))
(equal? x (begin (set-cdr! x x)
x )) ) ;
Taxonomie:
valeur |
composite |
terminale |
muable |
cons, vector, string |
fermeture |
immuable |
symbole, "..." (possiblement) |
nombre, booléen, caractère, primitive |
Grands entiers alloués.
Une possible définition d'equal? (DrScheme est comme cela):
(define (equal? x y)
(or (eq? x y)
(cond ((pair? x)
(and (pair? y)
(equal? (car x) (car y))
(equal? (cdr x) (cdr y)) ) )
((number? x)
(and (number? y)
(= x y) ) )
...
(else #f) ) ) )
Si (eq? x y) est vrai alors
(equal? x y) ne peut être faux.
Cas (très) particulier: l'égalité des fonctions.
f = g Þ " x, f(x) = g(x)
La réciproque est connue sous le nom d'axiome d'extensionnalité en
l-calcul.
(let ((f (lambda (x) (car x))))
(eq? f f) ) ;
(eq? car car)
(eq? cons cons)
(equal? cons cons)
Planche : 6
Consommation
(define (append l1 l2)
(if (pair? l1)
(cons (car l1)
(append (cdr l1) l2) )
l2 ) )
La fonction append a un coût linéaire en son premier argument.
Planche : 7
Efficacité
(iota n) ® (0 1 2 ... n-1)
(define (iota1 n)
(if (> n 0)
(append (iota1 (- n 1))
(list (- n 1)) )
'() ) )
(define (iota12 n)
(if (> n 0)
(append! (iota12 (- n 1))
(list (- n 1)) )
'() ) )
(define (append! l1 l2)
(if (pair? l1)
(begin
(if (null? (cdr l1))
(set-cdr! l1 l2)
(append! (cdr l1) l2) )
l1 )
l2 ) )
(define (iota2 n)
(define (enum beg end)
(if (< beg end)
(cons beg (enum (+ beg 1) end))
'() ) )
(enum 0 n) )
Accrochage de bord:
(define (iota3 n)
(if (> n 0)
(let ((result (list 0)))
(define (add r)
(if (< (car r) (- n 1))
(begin
(set-cdr! r (list (+ 1 (car r))))
(add (cdr r)) ) ) )
(add result)
result )
'() ) )
Planche : 8
Affectation
(set! variable forme)
L'affectation commence par calculer la forme puis modifie
la variable en conséquence. La valeur que retourne une
affectation n'est pas spécifiée.
Scheme n'est donc pas un langage purement fonctionnel de par la
présence de l'affectation et de données muables.
En DrScheme, set! ne crée pas de variable globale.
L'affectation force à abandonner le modèle de calcul par substitution
au profit de la notion de liaison. En effet:
(let* ((w+r (let ((shared '(hello world)))
(cons (lambda (v)
(set! shared v) )
(lambda () shared) ) ))
(reader (cdr w+r))
(writer (car w+r)) )
(reader) ;
(writer '(salut monde))
(reader) ) ;
(define (make-enumerator)
(let ((seed 0))
(lambda ()
(let ((current seed))
(set! seed (+ seed 1))
current ) ) ) )
(define N1 (make-enumerator))
(define N2 (make-enumerator))
(list (N1) (N1) (N1) (N1) (N2)) ;
La fonction enumerator pourrait être réécrite de façon
équivalente en:
(define make-enumerator
(lambda ()
(let ((seed 0))
(lambda ()
(let ((current seed))
(set! seed (+ seed 1))
current ) ) ) ) )
mais pas du tout comme:
(define make-enumerator
(let ((seed 0))
(lambda ()
(lambda ()
(let ((current seed))
(set! seed (+ seed 1))
current ) ) ) ) )
Planche : 9
La puissance de l'affectation
let était une syntaxe non primitive, letrec aussi:
(letrec ((variable1 forme1)
...
(variablen formen) )
forme... )
est équivalent à
(let ((variable1 'wait)
...
(variablen 'wait) )
(set! variable1 forme1)
...
(set! variablen formen)
forme... )
Planche : 10
Mort aux cons ou, le style par passage de message
Comment simuler des paires avec l en message-passing
style ?
(define (kons a d)
(lambda (msg)
(case msg
((car) a)
((cdr) d)
((set-car!) (lambda (v) (set! a v)))
((set-cdr!) (lambda (v) (set! d v)))
(else (error 'kons "incompris" msg)) ) ) )
(define (kar p)
(p 'car) )
(define (set-kar! p v)
((p 'set-car!) v) )
(define unCons (kons 1 2))
(set-kar! unCons 3)
(kar unCons) ;
Une paire pointée est représentée par une fermeture.
Planche : 11
Promesses ou glaçons
(delay forme) ;
(force promesse) ;
Une implantation possible:
(delay e) º (lambda () e)
(define (force promesse)
(promesse) )
On peut améliorer delay pour ne calculer au plus qu'une fois
la valeur d'une promesse.
(delay e) º (memo (lambda () e))
(define (force promesse) (promesse))
(define (memo thunk)
(let ((computed? #f)
(value 'wait) )
(lambda ()
(if computed?
value
(begin (set! value (thunk))
(set! computed? #t)
value ) ) ) ) )
Planche : 12
Programmation par flots
(head flot) rend le premier terme du flot
(tail flot) rend un flot privé de son premier élément
(empty-stream? flot) teste si un flot est vide
(cons-stream valeur flot) crée un flot
(empty-stream) crée le flot vide
Une première implantation:
(define head car)
(define tail cdr)
(define empty-stream? null?)
(define cons-stream cons)
(define (empty-stream) '())
Planche : 13
Somme des feuilles impaires
énumérer les feuilles => (flot de feuilles)
=> filtrer les feuilles => (flot d'impairs)
=> accumuler les résultats
(define (tree->stream tree)
(if (pair? tree)
(append-stream
(tree->stream (car tree))
(tree->stream (cdr tree)) )
(cons-stream tree (empty-stream)) ) )
Quelques utilitaires
(define (append-stream stream1 stream2)
(if (empty-stream? stream1)
stream2
(cons-stream (head stream1)
(append-stream (tail stream1)
stream2 ) ) ) )
(define (stream-filter predicate stream)
(if (empty-stream? stream)
(empty-stream)
(if (predicate (head stream))
(cons-stream
(head stream)
(stream-filter predicate
(tail stream) ) )
(stream-filter predicate
(tail stream) ) ) ) )
Accumulation:
(define (stream-reduce f end stream)
(if (empty-stream? stream)
end
(f (head stream)
(stream-reduce f end (tail stream)) ) ) )
(define (stream-nth n stream)
(if (= n 0)
(head stream)
(stream-nth (- n 1) (tail stream)) ) )
Une solution (enfin):
(define (count-odd-leaves tree)
(stream-reduce
+
0
(stream-filter
(lambda (leaf)
(and (number? leaf) (odd? leaf)) )
(tree->stream tree) ) ) )
Moralité: moins de récursion, plus d'itérations (à la STL).
Planche : 14
Le flot des entiers
Et si l'on écrivait:
(define (entiers start)
(cons start (entiers (+ start 1))) )
(0 1 2 3 ...)
(cadr (entiers 0))
Écrivons alors:
(define (integers start)
(cons-stream start (integers (+ start 1))) )
(head (tail (integers 0)))
(0 . #<entiers à partir de 1>)
(0 1 . #<entiers à partir de 2>)
Planche : 15
Seconde implantation
(define head car)
(define empty-stream? null?)
(define (empty-stream) '())
(define-macro (cons-stream elt s)
`(cons ,elt (delay ,s)) )
(define (tail s)
(force (cdr s)) )
Planche : 16
Quelques flots
Le flot des nombres de Fibonacci:
Fn+2 = Fn + Fn+1,
F1 = F0 = 1
(define (generate-fibonacci fn fn+1)
(cons-stream
fn
(generate-fibonacci fn+1 (+ fn fn+1)) ) )
Les entiers revisités:
(define (map-stream f stream)
(if (empty-stream? stream)
(empty-stream)
(cons-stream (f (head stream))
(map-stream f (tail stream)) ) ) )
(define integers
(cons-stream 0
(map-stream (lambda (n) (+ n 1))
integers ) ) )
Ce document a été traduit de LATEX par
HEVEA.