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