Les langages de programmation fonctionnelle et Scheme en particulier permettent de faire de la programmation d'ordre supérieure. Cela consiste à manipuler des fonctions comme on manipule d'autres types de données. C'est à dire qu'une fonction peut avoir des fonctions comme arguments et comme résultat. Cette caractéristique permet entre autre de faire de l'abstraction de fonctions.
Écrivons une fonction qui multiplie par deux chaque élément d'une liste de nombres :
(define fois2 (lambda (l) (if (null? l) '() (cons (* 2 (car l)) (fois2 (cdr l))))))
Si maintenant on veut une fonction qui calcule la racine carrée de chaque élément, on écrit :
(define racine-tous (lambda (l) (if (null? l) '() (cons (sqrt (car l)) (racine-tous (cdr l))))))
En observant ces deux fonctions, on voit qu'elles ont le même schéma :
(define NOMFONC (lambda (l) (if (null? l) '() (cons (FONCTION (car l)) (NOMFONC (cdr l))))))
Maintenant, si l'on veut une fonction qui élève au carré chaque élément, ou bien qui prend l'inverse de chaque élément, il faut à chaque fois écrire une fonction différente mais dont le schéma est le même.
La programmation d'ordre supérieure nous permet de définir une fonction qui applique une fonction à chaque élément d'une liste. On conserve le schéma général des deux fonctions précédentes et on ajoute un paramètre supplémentaire qui correspond à la fonction que l'on veut appliquer à chaque élément de la liste :
(define applique-a-tous (lambda (f l) (if (null? l) '() (cons (f (car l)) (applique-a-tous f (cdr l))))))
La fonction applique-a-tous est une abstraction des fonctions fois2, racine-tous, ...On peut récrire ces fonctions de la manière suivante :
(define fois2 (lambda (l) (applique-a-tous (lambda (x) (* 2 x)) l)))
(define racine-tous (lambda (l) (applique-a-tous sqr l)))
Et si on veut calculer le cube de chacun des éléments on écrit :
(define tous-au-cube (lambda (l) (applique-a-tous (lambda (x) (* x x x)) l)))
La fonction applique-a-tous est tellement utile qu'elle est prédéfinie en Scheme. Elle s'appelle map et constitue une version plus générale de applique-a-tous car elle peut s'utiliser avec plusieurs listes. Dans ce cas la lambda-expression que l'on passe en argument doit avoir autant de paramètres qu'il y a de listes. Si les listes ont des longueurs différentes, map s'arrête dès que la plus petite liste est vide.
Ex :
: (map (lambda (x) (+ x 10)) '(1 2 3 4 5)) (11 12 13 14 15) : (map (lambda (x y) (+ x y)) '(1 2 3 4) '(10 20 30 40)) (11 22 33 44) : (map (lambda (a) (cons a '())) '(1 a 2 b)) ((1)(a)(2)(b)) : (map (lambda (a b c) (list a b c)) '(1 2 3) '(a b) '(v w x y z)) ((1 a v)(2 b w))
Ex 1 : Composition de fonctions
Étant données deux fonctions et , on veut construire la fonction composée : . On définit une fonction compose qui prend comme paramètres deux fonctions f et g et rend une fonction.
compose : | Fonction, Fonction | Fonction | |
f, g |
(define compose (lambda (f g) (lambda (x) (f (g x))) ))
Exemple d'utilisation :
: (define carre (lambda (x) (* x x))) carre : (define plus1 (lambda (x) (+ 1 x))) plus1 :(compose carre plus1) #[procedure] :( (compose carre plus1) 4) 25
Ex 2 : Puissance nième d'une fonction
Étant donnée une fonction d'arité 1, , on veut construire la puissance nième de f : .
On définit une fonction compose-n-fois dont la spécification est :
compose-n-fois : | Fonction, Entier | Fonction | |
f, g |
(define compose-n-fois (lambda (f n) (if (= n 1) f (lambda (x) (f ((compose-n-fois f (- n 1)) x))) )))
Ex 3 : Dérivée d'une fonction
Étant donnée une fonction f, on veut construire une fonction qui calcule une approximation de la dérivée de f. Pour dx "petit", on définit la fonction dérivée f' par :
(define derive (lambda (f dx) (lambda (x) (/ (- (f (+ x dx)) (f x)) dx))))
Exemple d'utilisation :
: (define cube (lambda (x) (* x x x))) cube : ( (derive cube 0.0001) 10) 300.00300000893