From a5b0fff0dbac76a85c2fbfb78b0ba1cc0a4019c3 Mon Sep 17 00:00:00 2001 From: Antoine POINTEAU Date: Thu, 2 Dec 2021 23:28:37 +0100 Subject: [PATCH 1/7] add fr tour higher order functions --- _fr/tour/higher-order-functions.md | 113 ++++++++++++++++++++++++++++- 1 file changed, 112 insertions(+), 1 deletion(-) diff --git a/_fr/tour/higher-order-functions.md b/_fr/tour/higher-order-functions.md index a26e90f16f..b5aded7404 100644 --- a/_fr/tour/higher-order-functions.md +++ b/_fr/tour/higher-order-functions.md @@ -3,10 +3,121 @@ layout: tour title: Higher-order Functions partof: scala-tour -num: 7 +num: 10 language: fr next-page: nested-functions previous-page: mixin-class-composition --- + +Les fonctions d'ordre supérieur prennent d'autres fonctions en paramètres ou retournent une fonction en résultat. +C'est possible car les fonctions sont des valeurs de première classe en Scala. +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. + +Dans le monde du pure orienté objet une bonne pratique est d'éviter d'exposer des méthodes paramétrisés avec des fonctions qui pourraient laisser fuiter l'état interne de l'objet. La fuite de l'état interne de l'objet pourrait casser les invariants de l'objet lui-même ce qui violerait l'encapsulation. + +Un des exemples les plus communs est la fonction d'ordre supérieur `map` qui est diponible pour les collections en Scala. + +```scala mdoc +val salaries = Seq(20000, 70000, 40000) +val doubleSalary = (x: Int) => x * 2 +val newSalaries = salaries.map(doubleSalary) // List(40000, 140000, 80000) +``` + +`doubleSalary` est une fonction qui prend un seul entier, `x` et retourne `x * 2`. La partie à gauche de la flèche `=>` est le paramètre de la liste 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. + +Pour réduire le code, nous pouvons faire une fonction anonyme et la passer directement en argument de map : + +```scala:nest +val salaries = Seq(20000, 70000, 40000) +val newSalaries = salaries.map(x => x * 2) // List(40000, 140000, 80000) +``` + +Notez comment `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 le fonction map souhaiterait. (voir [Currying](/tour/multiple-parameter-lists.html)). Une autre façon d'écrire le même morceau de code encore plus idiomatique serait : + +```scala mdoc:nest +val salaries = Seq(20000, 70000, 40000) +val newSalaries = salaries.map(_ * 2) +``` + +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. +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). + +## Convertir les méthodes en fonctions + +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. + +```scala mdoc +case class WeeklyWeatherForecast(temperatures: Seq[Double]) { + + private def convertCtoF(temp: Double) = temp * 1.8 + 32 + + def forecastInFahrenheit: Seq[Double] = temperatures.map(convertCtoF) // <-- passing the method convertCtoF +} +``` + +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). + +## Les fonction qui acceptent des fonctions + +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 : + +```scala mdoc +object SalaryRaiser { + + def smallPromotion(salaries: List[Double]): List[Double] = + salaries.map(salary => salary * 1.1) + + def greatPromotion(salaries: List[Double]): List[Double] = + salaries.map(salary => salary * math.log(salary)) + + def hugePromotion(salaries: List[Double]): List[Double] = + salaries.map(salary => salary * salary) +} +``` + +Notez comment chacunes de ces trois méthodes ne changent que par le facteur de multiplication. +Pour simplifier, vous pouvez extraire et répéter le code dans une fonction d'ordre supérieur comme ceci : + +```scala mdoc:nest +object SalaryRaiser { + + private def promotion(salaries: List[Double], promotionFunction: Double => Double): List[Double] = + salaries.map(promotionFunction) + + def smallPromotion(salaries: List[Double]): List[Double] = + promotion(salaries, salary => salary * 1.1) + + def greatPromotion(salaries: List[Double]): List[Double] = + promotion(salaries, salary => salary * math.log(salary)) + + def hugePromotion(salaries: List[Double]): List[Double] = + promotion(salaries, salary => salary * salary) +} +``` + +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. + +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. + +## Les fonctions qui retournent des fonctions + +Il y a certains cas ou vous voulez générer une fonction. Voici un exemple de méthode qui retourne une fonction. + +```scala mdoc +def urlBuilder(ssl: Boolean, domainName: String): (String, String) => String = { + val schema = if (ssl) "https://" else "http://" + (endpoint: String, query: String) => s"$schema$domainName/$endpoint?$query" +} + +val domainName = "www.example.com" +def getURL = urlBuilder(ssl=true, domainName) +val endpoint = "users" +val query = "id=1" +val url = getURL(endpoint, query) // "https://www.example.com/users?id=1": String +``` + +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"` + +Traduit par Antoine Pointeau. \ No newline at end of file From 64aef59c5dbb368f13d6965f1f33a26217e4805c Mon Sep 17 00:00:00 2001 From: Antoine Pointeau Date: Wed, 5 Jan 2022 15:37:57 +0100 Subject: [PATCH 2/7] Update _fr/tour/higher-order-functions.md Co-authored-by: Julien Richard-Foy --- _fr/tour/higher-order-functions.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/_fr/tour/higher-order-functions.md b/_fr/tour/higher-order-functions.md index b5aded7404..38cf18c306 100644 --- a/_fr/tour/higher-order-functions.md +++ b/_fr/tour/higher-order-functions.md @@ -15,7 +15,7 @@ Les fonctions d'ordre supérieur prennent d'autres fonctions en paramètres ou r C'est possible car les fonctions sont des valeurs de première classe en Scala. 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. -Dans le monde du pure orienté objet une bonne pratique est d'éviter d'exposer des méthodes paramétrisés avec des fonctions qui pourraient laisser fuiter l'état interne de l'objet. La fuite de l'état interne de l'objet pourrait casser les invariants de l'objet lui-même ce qui violerait l'encapsulation. +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. Un des exemples les plus communs est la fonction d'ordre supérieur `map` qui est diponible pour les collections en Scala. From f390b7d67b499a9911f6dcfa73394b2a19f7cb4b Mon Sep 17 00:00:00 2001 From: Antoine Pointeau Date: Wed, 5 Jan 2022 15:38:26 +0100 Subject: [PATCH 3/7] Update _fr/tour/higher-order-functions.md Co-authored-by: Julien Richard-Foy --- _fr/tour/higher-order-functions.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/_fr/tour/higher-order-functions.md b/_fr/tour/higher-order-functions.md index 38cf18c306..90c0cb1ac4 100644 --- a/_fr/tour/higher-order-functions.md +++ b/_fr/tour/higher-order-functions.md @@ -25,7 +25,7 @@ val doubleSalary = (x: Int) => x * 2 val newSalaries = salaries.map(doubleSalary) // List(40000, 140000, 80000) ``` -`doubleSalary` est une fonction qui prend un seul entier, `x` et retourne `x * 2`. La partie à gauche de la flèche `=>` est le paramètre de la liste 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. +`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. Pour réduire le code, nous pouvons faire une fonction anonyme et la passer directement en argument de map : From a4906b38831e42119ded33844b4de95fb20ac381 Mon Sep 17 00:00:00 2001 From: Antoine Pointeau Date: Wed, 5 Jan 2022 15:38:32 +0100 Subject: [PATCH 4/7] Update _fr/tour/higher-order-functions.md Co-authored-by: Julien Richard-Foy --- _fr/tour/higher-order-functions.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/_fr/tour/higher-order-functions.md b/_fr/tour/higher-order-functions.md index 90c0cb1ac4..cd46a21ddb 100644 --- a/_fr/tour/higher-order-functions.md +++ b/_fr/tour/higher-order-functions.md @@ -27,7 +27,7 @@ val newSalaries = salaries.map(doubleSalary) // List(40000, 140000, 80000) `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. -Pour réduire le code, nous pouvons faire une fonction anonyme et la passer directement en argument de map : +Pour réduire le code, nous pouvons faire une fonction anonyme et la passer directement en argument de `map` : ```scala:nest val salaries = Seq(20000, 70000, 40000) From 2e48697666eb9594e8b44803c2ccf918faac98cb Mon Sep 17 00:00:00 2001 From: Antoine Pointeau Date: Wed, 5 Jan 2022 15:39:11 +0100 Subject: [PATCH 5/7] Update _fr/tour/higher-order-functions.md Co-authored-by: Julien Richard-Foy --- _fr/tour/higher-order-functions.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/_fr/tour/higher-order-functions.md b/_fr/tour/higher-order-functions.md index cd46a21ddb..4fca43d80a 100644 --- a/_fr/tour/higher-order-functions.md +++ b/_fr/tour/higher-order-functions.md @@ -41,7 +41,7 @@ val salaries = Seq(20000, 70000, 40000) val newSalaries = salaries.map(_ * 2) ``` -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. +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. 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). ## Convertir les méthodes en fonctions From cb8dd549808255747a55e725661535de9a0f9749 Mon Sep 17 00:00:00 2001 From: Antoine Pointeau Date: Wed, 5 Jan 2022 15:39:42 +0100 Subject: [PATCH 6/7] Update _fr/tour/higher-order-functions.md Co-authored-by: Julien Richard-Foy --- _fr/tour/higher-order-functions.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/_fr/tour/higher-order-functions.md b/_fr/tour/higher-order-functions.md index 4fca43d80a..77b3f4efdd 100644 --- a/_fr/tour/higher-order-functions.md +++ b/_fr/tour/higher-order-functions.md @@ -78,7 +78,7 @@ object SalaryRaiser { ``` Notez comment chacunes de ces trois méthodes ne changent que par le facteur de multiplication. -Pour simplifier, vous pouvez extraire et répéter le code dans une fonction d'ordre supérieur comme ceci : +Pour simplifier, vous pouvez extraire le code répété dans une fonction d'ordre supérieur comme ceci : ```scala mdoc:nest object SalaryRaiser { From 0d5c5cf014957857e44de5adcfd3364b14ea1cbb Mon Sep 17 00:00:00 2001 From: Julien Richard-Foy Date: Wed, 5 Jan 2022 16:05:02 +0100 Subject: [PATCH 7/7] Update _fr/tour/higher-order-functions.md --- _fr/tour/higher-order-functions.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/_fr/tour/higher-order-functions.md b/_fr/tour/higher-order-functions.md index 77b3f4efdd..513f6b619f 100644 --- a/_fr/tour/higher-order-functions.md +++ b/_fr/tour/higher-order-functions.md @@ -34,7 +34,7 @@ val salaries = Seq(20000, 70000, 40000) val newSalaries = salaries.map(x => x * 2) // List(40000, 140000, 80000) ``` -Notez comment `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 le fonction map souhaiterait. (voir [Currying](/tour/multiple-parameter-lists.html)). Une autre façon d'écrire le même morceau de code encore plus idiomatique serait : +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 : ```scala mdoc:nest val salaries = Seq(20000, 70000, 40000)