4. Fonctions d'Ordre Supérieur
Les Fonctions sont des Valeurs
En Gleam, une fonction est une valeur comme une autre. On peut la stocker dans une variable, la passer en paramètre, la retourner.
// Un entier
let x: Int = 42
// Une fonction - même statut que x
let double: fn(Int) -> Int = fn(n) { n * 2 }
// On peut passer une fonction en paramètre
pub fn appliquer(f: fn(Int) -> Int, valeur: Int) -> Int {
f(valeur)
}
appliquer(double, 5) // -> 10
Fonctions Anonymes (Lambdas)
Une fonction anonyme est une fonction sans nom, définie sur place avec fn.
// Fonction nommée
pub fn double(x: Int) -> Int { x * 2 }
// Fonction anonyme équivalente
fn(x) { x * 2 }
Exemples
fn(x) { x + 1 } // ajoute 1
fn(x) { x * x } // carré
fn(x) { x > 0 } // teste si positif
fn(x, y) { x + y } // addition (2 paramètres)
Pourquoi utiliser des lambdas ?
Cas typique : fonction utilisée une seule fois
// Définir une fonction nommée juste pour ça : verbeux
pub fn double(x: Int) -> Int { x * 2 }
let resultat = appliquer(double, 5)
// Lambda : plus direct
let resultat = appliquer(fn(x) { x * 2 }, 5)
Fonctions d'Ordre Supérieur
Une fonction d'ordre supérieur prend une ou plusieurs fonctions en paramètres.
map : Transformer Chaque Élément
D'abord, écrivons-la à la main :
pub fn map(f: fn(a) -> b, lst: Liste(a)) -> Liste(b) {
case lst {
Vide -> Vide
Cons(tete, queue) -> Cons(f(tete), map(f, queue))
// ↑
// on transforme la tête
}
}
Utilisation :
map(fn(x) { x * 2 }, Cons(1, Cons(2, Cons(3, Vide))))
// -> [2, 4, 6]
map(fn(x) { x > 5 }, Cons(3, Cons(7, Cons(2, Vide))))
// -> [False, True, False]
Visualisation :
Avec le pipe :
filter : Garder Certains Éléments
D'abord, à la main :
pub fn filter(pred: fn(a) -> Bool, lst: Liste(a)) -> Liste(a) {
case lst {
Vide -> Vide
Cons(tete, queue) ->
case pred(tete) {
True -> Cons(tete, filter(pred, queue)) // on garde
False -> filter(pred, queue) // on jette
}
}
}
Utilisation :
filter(fn(x) { x > 5 }, Cons(3, Cons(7, Cons(2, Cons(9, Vide)))))
// -> [7, 9]
filter(fn(x) { x % 2 == 0 }, Cons(1, Cons(2, Cons(3, Cons(4, Vide)))))
// -> [2, 4]
Trace d'exécution :
filter (x > 5) [3, 7, 2, 9]
= 3 > 5 ? Non → on jette, filter [7, 2, 9]
= 7 > 5 ? Oui → Cons(7, filter [2, 9])
= 2 > 5 ? Non → on jette, filter [9]
= 9 > 5 ? Oui → Cons(9, filter [])
= []
= [7, 9]
Avec le pipe :
lst
|> filter(fn(x) { x % 2 == 0 }) // garde les pairs
|> map(fn(x) { x * x }) // met au carré
|> somme // additionne
map et filter Combinés
Ces deux fonctions se composent naturellement via le pipe :
// Somme des carrés des nombres pairs
pub fn sommeCarresPairs(lst: Liste(Int)) -> Int {
lst
|> filter(fn(x) { x % 2 == 0 }) // [2, 4, 6]
|> map(fn(x) { x * x }) // [4, 16, 36]
|> somme // 56
}
Ce Que Vous Avez Compris
En écrivant map et filter à la main, vous avez remarqué qu'elles suivent le même schéma que toutes vos fonctions récursives :
Ce pattern a un nom en mathématiques : catamorphisme. En pratique, il est capturé par une fonction appelée fold. C'est une abstraction puissante, mais ce n'est pas l'objet de ce cours. Ce qui compte ici : vous en avez compris l'essence en l'écrivant vous-mêmes.
Résumé
| Concept | Syntaxe | Rôle |
|---|---|---|
| Lambda | fn(x) { ... } |
Fonction sans nom, sur place |
| Fonction d'ordre supérieur | fn(f: fn(a) -> b, ...) |
Prend une fonction en paramètre |
map |
map(fn(x) { ... }, lst) |
Transforme chaque élément |
filter |
filter(fn(x) { ... }, lst) |
Garde certains éléments |