Skip to content

Commit c3ec734

Browse files
authored
add fr tour higher order functions (#2253)
1 parent eac68ef commit c3ec734

File tree

1 file changed

+112
-1
lines changed

1 file changed

+112
-1
lines changed

_fr/tour/higher-order-functions.md

+112-1
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,121 @@ layout: tour
33
title: Higher-order Functions
44
partof: scala-tour
55

6-
num: 7
6+
num: 10
77

88
language: fr
99

1010
next-page: nested-functions
1111
previous-page: mixin-class-composition
1212
---
13+
14+
Les fonctions d'ordre supérieur prennent d'autres fonctions en paramètres ou retournent une fonction en résultat.
15+
C'est possible car les fonctions sont des valeurs de première classe en Scala.
16+
La terminologie peut devenir une peu confuse à ce point, et nous utilisons l'expression "fonction d'ordre supérieur" à la fois pour les méthodes et les fonctions qui prennent d'autres fonctions en paramètres ou retournent une fonction en résultat.
17+
18+
Dans le monde du pur orienté objet, une bonne pratique est d'éviter d'exposer des méthodes paramétrées avec des fonctions qui pourraient exposer l'état interne de l'objet. Le fait d’exposer l'état interne de l'objet pourrait casser les invariants de l'objet lui-même ce qui violerait l'encapsulation.
19+
20+
Un des exemples les plus communs est la fonction d'ordre supérieur `map` qui est diponible pour les collections en Scala.
21+
22+
```scala mdoc
23+
val salaries = Seq(20000, 70000, 40000)
24+
val doubleSalary = (x: Int) => x * 2
25+
val newSalaries = salaries.map(doubleSalary) // List(40000, 140000, 80000)
26+
```
27+
28+
`doubleSalary` est une fonction qui prend un seul entier, `x` et retourne `x * 2`. La partie à gauche de la flèche `=>` est la liste de paramètres, et la valeur de l'expression à droite est ce qui est retourné. Sur la ligne 3, la fonction `doubleSalary` est appliquée à chaque élément dans la liste des salariés.
29+
30+
Pour réduire le code, nous pouvons faire une fonction anonyme et la passer directement en argument de `map` :
31+
32+
```scala:nest
33+
val salaries = Seq(20000, 70000, 40000)
34+
val newSalaries = salaries.map(x => x * 2) // List(40000, 140000, 80000)
35+
```
36+
37+
Notez que `x` n'est pas déclaré comme un `Int` dans l'exemple ci-dessus. C'est parce que le compilateur peut inférrer le type en se basant sur le type que méthode `map` attend. (voir [Currying](/tour/multiple-parameter-lists.html)). Une autre façon d'écrire le même morceau de code encore plus idiomatique serait :
38+
39+
```scala mdoc:nest
40+
val salaries = Seq(20000, 70000, 40000)
41+
val newSalaries = salaries.map(_ * 2)
42+
```
43+
44+
Sachant que le compilateur Scala sait déjà quel est le type des paramètres (un seul `Int`), vous pouvez fournir uniquement la partie de droite de la fonction.
45+
La seule contrepartie c'est que vous devez utiliser `_` à la place du nom du paramètre (c'était `x` dans l'exemple précédent).
46+
47+
## Convertir les méthodes en fonctions
48+
49+
Il est aussi possible de passer des méthodes comme arguments aux fonctions d'ordre supérieur, parce que le compilateur Scala va convertir la méthode en fonction.
50+
51+
```scala mdoc
52+
case class WeeklyWeatherForecast(temperatures: Seq[Double]) {
53+
54+
private def convertCtoF(temp: Double) = temp * 1.8 + 32
55+
56+
def forecastInFahrenheit: Seq[Double] = temperatures.map(convertCtoF) // <-- passing the method convertCtoF
57+
}
58+
```
59+
60+
Ici la méthode `convertCtoF` est passée à la fonction d'ordre supérieur `map`. C'est possible car le compilateur convertit `convertCtoF` vers la fonction `x => convertCtoF(x)` (note : `x` sera un nom généré qui sera garanti d'être unique dans le scope).
61+
62+
## Les fonction qui acceptent des fonctions
63+
64+
Une raison d'utiliser les fonctions d'ordre supérieur est de réduire le code redondant. Suposons que vous souhaitez des méthodes qui augmentent le salaire de quelqu'un en fonction de différents facteurs. Sans créer de fonction d'ordre supérieur, cela ressemblerait à ça :
65+
66+
```scala mdoc
67+
object SalaryRaiser {
68+
69+
def smallPromotion(salaries: List[Double]): List[Double] =
70+
salaries.map(salary => salary * 1.1)
71+
72+
def greatPromotion(salaries: List[Double]): List[Double] =
73+
salaries.map(salary => salary * math.log(salary))
74+
75+
def hugePromotion(salaries: List[Double]): List[Double] =
76+
salaries.map(salary => salary * salary)
77+
}
78+
```
79+
80+
Notez comment chacunes de ces trois méthodes ne changent que par le facteur de multiplication.
81+
Pour simplifier, vous pouvez extraire le code répété dans une fonction d'ordre supérieur comme ceci :
82+
83+
```scala mdoc:nest
84+
object SalaryRaiser {
85+
86+
private def promotion(salaries: List[Double], promotionFunction: Double => Double): List[Double] =
87+
salaries.map(promotionFunction)
88+
89+
def smallPromotion(salaries: List[Double]): List[Double] =
90+
promotion(salaries, salary => salary * 1.1)
91+
92+
def greatPromotion(salaries: List[Double]): List[Double] =
93+
promotion(salaries, salary => salary * math.log(salary))
94+
95+
def hugePromotion(salaries: List[Double]): List[Double] =
96+
promotion(salaries, salary => salary * salary)
97+
}
98+
```
99+
100+
La nouvelle méthode, `promotion`, prend les salaires plus une fonction du type `Double => Double` (càd. une fonction qui prend un Double et retourne un Double) et retourne le produit.
101+
102+
Les méthodes et les fonctions expriment généralement des comportements ou des transformations de données, donc avoir des fonctions qui composent en se basant sur d'autres fonctions peut aider à construire des mécanismes génériques. Ces opérations génériques reportent le verrouillage de l'intégralité du comportement de l'opération, donnant aux clients un moyen de contrôler ou de personnaliser davantage certaines parties de l'opération elle-même.
103+
104+
## Les fonctions qui retournent des fonctions
105+
106+
Il y a certains cas ou vous voulez générer une fonction. Voici un exemple de méthode qui retourne une fonction.
107+
108+
```scala mdoc
109+
def urlBuilder(ssl: Boolean, domainName: String): (String, String) => String = {
110+
val schema = if (ssl) "https://" else "http://"
111+
(endpoint: String, query: String) => s"$schema$domainName/$endpoint?$query"
112+
}
113+
114+
val domainName = "www.example.com"
115+
def getURL = urlBuilder(ssl=true, domainName)
116+
val endpoint = "users"
117+
val query = "id=1"
118+
val url = getURL(endpoint, query) // "https://www.example.com/users?id=1": String
119+
```
120+
121+
Notez le type de retour de urlBuilder `(String, String) => String`. Cela veut dire que la fonction anonyme retournée prend deux Strings et retourne une String. Dans ce cas, la fonction anonyme retournée est `(endpoint: String, query: String) => s"https://www.example.com/$endpoint?$query"`
122+
123+
Traduit par Antoine Pointeau.

0 commit comments

Comments
 (0)