Programmation fonctionnelle

La programmation fonctionnelle est un paradigme de programmation qui insiste sur l’évaluation d’appel de fonctions plutôt que sur l’utilisation de variables et de blocs imbriqués (for, if…).

Les arrow functions

Les fonctions ont toujours été des objets de plein droit en JavaScript. Il est par exemple possible de créer des fonctions anonymes et de les affecter à des variables ou de les passer en paramètre d’autres fonctions.

let compare = function(a, b) {
    return a === b ? "pareil" : "pas pareil";
}

console.log(compare("Bonjour", "Au revoir")); // Affiche pas pareil

Depuis ES6, il existe une forme simplifiée pour la déclaration de fonction anonyme grâce à l’opérateur => qui vaut à ce type de fonction le nom de arrow functions.

let compare = (a, b) => {
    return a === b ? "pareil" : "pas pareil";
}

console.log(compare("Bonjour", "Au revoir")); // Affiche pas pareil

Lorsque la fonction ne contient qu’une seule instruction, les accolades ne sont pas obligatoires et le mot-clé return peut être omis car on considère que la fonction retourne le résultat de l’évaluation de l’instruction.

let compare = (a, b) => a === b ? "pareil" : "pas pareil";

console.log(compare("Bonjour", "Au revoir")); // Affiche pas pareil

Note

Si la fonction ne prend qu’un seul paramètre, on peut aussi omettre les parenthèses autour du paramètre.

let negation = x => -x;

console.log(negation(1)); // Affiche -1

Note

Dans la plupart des autres langages de programmation (comme C++, Python, Java, C#…), ce type de fonctions est appelé une lambda.

Les arrow functions sont surtout utilisées comme handlers ou comme callbacks en paramètres d’autres fonctions car elles permettent une écriture plus concise. Par exemple, la méthode Array.some attend une fonction en paramètre et retourne true si au moins un appel à cette fonction en passant un élément du tableau retourne true.

let tableau = [1, 3, 5, 8, 11];

let aUnElementPair = tableau.some(e => e % 2 == 0);

console.log(aUnElementPair); // Affiche true

Pour l’exemple précédent, il est plus simple et plus direct de passer une arrow function en paramètre de la méthode Array.some pour vérifier si au moins un élément est pair.

L’utilisation de forEach

Une première application de la programmation fonctionnelle consiste à repenser le parcours de boucle sous la forme d’un appel à la méthode Array.forEach à laquelle on passe une fonction à appeler pour chaque élément.

let t = [1, 2, 3, 4];
t.forEach(e => console.log(e));

Le modèle filter/map/reduce

Un modèle classique de traitement d’un tableau d’éléments dans une approche fonctionnelle consiste à envisager trois opérations fondamentales :

filter

permet de ne retenir qu’une partie des éléments d’un tableau.

map

permet de changer la nature des éléments lors du traitement.

reduce

permet de réduire l’ensemble des éléments à une seule valeur grâce à l’utilisation d’un accumulateur.

JavaScript fournit depuis ES5 les méthodes Array.filter, Array.map et Array.reduce qui attendent toutes en paramètre une fonction à appliquer pour chaque élément du tableau.

Si on désire filtrer les éléments d’un tableau pour créer un autre tableau, on peut utiliser la méthode Array.filter.

let t = [1, 2, 3, 4];
let impairs = t.filter(x => x % 2);
console.log(impairs); // Affiche [1, 3]

Si on veut modifier les éléments d’un tableau en créant un nouveau tableau, on peut utiliser la méthode Array.map. Le code ci-dessous, permet de créer un tableau de nombres à partir d’un tableau de chaînes de caractères.

let t = ["2", "101", "324"];
let nombres = t.map(x => parseInt(x, 10));
console.log(nombres); // Affiche [2, 101, 324]

Si on veut créer un résultat unique à partir d’un tableau, on peut utiliser la méthode Array.reduce. Cette dernière attend en paramètre une fonction qui prend deux paramètres : l’élément courant et l’élément résultat de l’appel précédent. On parle d’une fonction accumulatrice.

let t = [1, 2, 3];
let resultat = t.reduce((a, b) => a + b);
console.log(resultat); // Affiche 6

On peut chaîner ces méthodes pour obtenir des traitements complexes.

let celsius = [0, 12, -2, 6, -18, 32];

let min_farenheit = celsius.filter(x => x > 0)
                           .map(x => 1.8 * x + 32)
                           .reduce((x, y) => x < y ? x : y);

console.log(min_farenheit); // Affiche 48.8

Le code ci-dessous parcourt un tableau de températures en degré Celsius. On élimine les températures négatives ou égales à zéro, puis on transforme les températures en degré Fahrenheit. Enfin on conserve la température minimale par réduction. Donc, on affiche la température en degré Fahrenheit qui est la plus petite mais qui est positive.