Maison  >  Article  >  interface Web  >  Introduction et résumé de la programmation fonctionnelle (avec code)

Introduction et résumé de la programmation fonctionnelle (avec code)

yulia
yuliaoriginal
2018-09-11 17:01:311749parcourir

Récemment, j'ai lu les classiques d'introduction à la programmation fonctionnelle, et je les ai résumés moi-même. Si vous êtes intéressé ou en avez besoin, vous pouvez y jeter un œil.

1. Qu'est-ce que la programmation fonctionnelle

La programmation fonctionnelle est principalement basée sur les fonctions mathématiques et ses idées, passons donc d'abord en revue les fonctions en mathématiques
y = f (x)
C'est-à-dire que la fonction f(x) prend x comme paramètre et y comme résultat x et y peuvent être n'importe quel nombre, qui contient plusieurs points clés :
1. paramètre
2. Les fonctions doivent toujours renvoyer une valeur
3. Les fonctions doivent s'exécuter en fonction des paramètres qu'elles reçoivent (comme x), et non de l'environnement externe
4. Pour un x donné, n'affichera que le unique y
Comprenons à partir d'un exemple

// 一个计税函数
var percentValue = 5;
var calculateTax = (value) => {return value/100 * (100 + percentValue)};

Utiliser des fonctions en mathématiques pour analyser cette fonction, regardez d'abord le troisième élément, la fonction doit être basée sur les paramètres de réception pour s'exécuter au lieu du environnement extérieur. La fonction calculateTax s'appuie ici sur la percentValue externe. Par conséquent, cette fonction ne peut pas être appelée une fonction au sens mathématique. Alors, comment pouvons-nous la transformer en fonction au sens mathématique ? Très simple

var calculateTax = (value,percentValue) => {return value/100 * (100 + percentValue)};

Maintenant, cette fonction peut être appelée une vraie fonction.
Définissons maintenant la programmation fonctionnelle en termes techniques simples : la programmation fonctionnelle est un paradigme dans lequel une fonction peut compléter sa propre logique en s'appuyant uniquement sur les entrées et les sorties. Cela garantit que la fonction renvoie le même résultat lorsqu'elle est appelée plusieurs fois. La fonction ne modifie aucune variable d'environnement externe.

2. Transparence de référence

Selon la définition de la fonction, nous pouvons conclure que toutes les fonctions renverront la même valeur pour la même entrée et la même sortie. Cette propriété des fonctions est appelée transparence référentielle
À titre d'exemple

var identity = (i) => { return i };

Nous définissons une fonction simple qui ne s'appuie pas sur des variables globales à l'intérieur de la fonction, elle renvoie simplement l'entrée. Supposons maintenant qu'il soit appliqué entre d'autres appels de fonction comme

sum(4,5) + identity(1);

Selon la définition de la transparence référentielle, nous pouvons le convertir en

sum(4,5) + 1;

Ce processus est appelé modèle de remplacement, donc vous peut remplacer directement le résultat de la fonction (principalement parce que la logique de la fonction ne dépend pas d'autres variables globales).
Puisque la fonction renvoie la même valeur pour un résultat d'entrée donné, nous pouvons en fait la mettre en cache. Par exemple, nous avons une fonction "factorielle" pour calculer la factorielle. Il faut un argument pour calculer sa factorielle, par exemple la factorielle de 5 vaut 120. Lorsque l'utilisateur entre pour la deuxième fois la factorielle de 5, en raison de la transparence de référence (le même résultat est renvoyé pour la même entrée), nous savons que le résultat est 120, mais la machine ne le sait pas. la machine met en cache le résultat afin que les futurs appels puissent renvoyer directement le résultat au lieu d'avoir à le calculer à nouveau. Cela montre l'importance de la transparence référentielle et du code pouvant être mis en cache dans la programmation fonctionnelle.

3. Fonctionnel, déclaratif et abstrait

La programmation fonctionnelle prône la programmation déclarative et l'écriture de code abstrait
Qu'est-ce que la programmation déclarative
Supposons que nous voulions imprimer tous les éléments d'un tableau, nous pouvons utiliser la méthode suivante

var array = [1,2,3];
for(let i = 0; i < array.length; i++){
    console.log(array[i])
}

Dans ce code, nous indiquons au code exactement comment le faire. Tels que : obtenir la longueur du tableau, boucler le tableau et obtenir chaque élément par index. C’est une programmation impérative. La programmation impérative préconise de dire au compilateur quoi faire.
Regardons une autre manière

var arr = [1,2,3];
arr.forEach((ele) => { console.log(ele) })

Dans le code ci-dessus, nous avons supprimé l'obtention de la longueur du tableau, la boucle du tableau, l'utilisation de l'index pour obtenir les éléments du tableau, etc. Nous nous soucions uniquement de ce qu'il faut faire (c'est-à-dire l'impression des éléments du tableau), l'obtention de la longueur du tableau, le bouclage, etc. sont tous effectués par la machine pour nous. Nous devons uniquement nous soucier de ce qu'il faut faire, pas de comment le faire. Programmation déclarative
Programmation fonctionnelle La programmation prône la création de fonctions de manière abstraite qui peuvent être réutilisées ailleurs dans le code.

4. Fonction pure

Qu'est-ce qu'une fonction pure ? Une fonction fonctionnelle pure renvoie la même sortie pour une entrée donnée. Par exemple, le double ci-dessus

var double = (value) => value * 2;

est une fonction pure car elle renvoie toujours la même sortie pour la même entrée. Les fonctions pures obéissent à la transparence référentielle, on peut donc simplement remplacer double(5) par 10. Alors, quel est l’avantage des fonctions pures ? Jetons un coup d'oeil

1.4.1 Les fonctions pures produisent du code testable
Les fonctions impures ont des effets secondaires Prenons la fonction de calcul des taxes précédente comme exemple

var percentValue = 5;
var calculateTax = (value) => {return value/100 * (100 + percentValue)};

Cette fonction n'est pas. une fonction pure, principalement parce qu'elle dépend de l'environnement externe pour calculer sa logique. Lorsque l'environnement externe change, cela affectera le résultat. Par conséquent, la principale caractéristique d’une fonction pure est qu’elle ne dépend d’aucune variable externe et ne doit modifier aucune variable externe. Si vous modifiez des variables externes, cela peut entraîner des changements dans le comportement d'autres fonctions, c'est-à-dire produire des effets secondaires qui peuvent rendre le comportement du système imprévisible.

1.4.2 Code raisonnable
Nous devrions déduire la fonction de la fonction à travers son nom, comme la double fonction

var double = (value) => value * 2;

我们可以通过函数名轻易的推出这个函数会把给定的数值加倍,因此根据引用透明性,我们可以直接把 double(5) 替换成 10。还有一个例子,Math.max(3,4,5,6) 结果是什么?虽然我们只看到了函数的名字,但是我们很容易看出结果,我们看到实现了吗?并没有,为什么,就是因为 Math.max 是纯函数啊!!!

5、 并发代码

纯函数允许我们并发的执行代码,因为纯函数不会改变它的环境,这意味着我们根本不需要担心同步问题。当然,js 是单线程的,但是如果项目中使用了 webworker 来并发执行任务,该怎么办?或者有一段 Node 环境中的服务端代码需要并发的执行函数,又该怎么办呢?

// 非纯函数代码
let global = &#39;something&#39;
let function1 = (input) => {
    // 处理 input
    // 改变 global
    global = "somethingElse"
}
let function2 = () => {
    if(global === "something"){
        // 业务逻辑
    }
}

如果我们需要并发的执行 function1 和 function2,假设 function1 在 function2 之前执行,就会改变 function2 的执行结果,所以并发执行这些代码就会造成不良的影响,现在把这些函数改为纯函数。

let function1 = (input,global) => {
    // 处理 input
    // 改变 global
    global = "somethingElse"
}
let function2 = (global) => {
   if(global === "something"){
        // 业务逻辑
    }
}

此处我们把 global 作为两个函数的参数,让它们变成纯函数,这样并发执行的时候就不会有问题了。

6、可缓存

既然纯函数对于给定的输入总能返回相同的输出,那么我们就能缓存函数的输出,例如

var doubleCache = (value) => {
    const cache = {};
    return function(value){
        if(!cache[value]){
            cache[value] = value * 2
            console.log(&#39;first time&#39;)
        }
        return cache[value];
    }
}
var double = doubleCache();
double(2) // first time,4
double(2) // 4
// 或者直接使用立即执行函数
var double = ((value) => {
    const cache = {};
    return function(value){
        if(!cache[value]){
            cache[value] = value * 2
            console.log(&#39;first time&#39;)
        }
        return cache[value];
    }
})()
double(2) // first time,4
double(2) // 4

这个函数中,假设我们第一次输入 5,cache 中并没有,于是执行代码,由于闭包的存在,cache[5] = 10,第二次我们调用的时候,cache[5] 存在,所以直接 return 10,看到了吗?这就是纯函数的魅力!!!别忘记这是因为纯函数的引用透明性。

7、 管道与组合

纯函数应该被设计为一次只做一件事,并且根据函数名就知道它所做的事情。
比如 linux 系统下有很多日常任务的命令,如 cat 用于打印文件内容,grep 用于搜索文件,wc 用于计算行数,这些命令一次只解决一个问题,但是我们可以用管道或组合来完成复杂的任务。假设我们需要在一个文件中找到一个特定的名称并统计它的出现次数,在命令行要输入如下指令
cat jsBook | grep -i “composing” | wc
上面的命令通过组合多个函数解决了我们的问题。组合不是 linux 命令独有的,它们是函数式编程范式的核心。
我们把它们称为函数式组合。来看一个 compose 函数的例子

var add1 = (value) =>{ return value+1 };
var double = (value) => {return value*2 };
var compose = (a,b) => {
    return (c) => {
       return a(b(c));
    }
}
var doubleAndAdd1 = compose(add1,double);
doubleAndAdd1(5) // 打印 5 * 2 + 1 = 11

compose 函数返回一个函数,将 b 的结果作为 a 的参数,这里就是将 double 的结果作为 add1 的参数,来实现了函数的组合。

8、 纯函数是数学函数

还记得我们之前的缓存函数吗,假设我们多次调用 double 对象,那么 cache 中就会变成这样

{
    1: 2,
    2: 4,
    3: 6,
    4: 8,
    5: 10
}

假设我们设置 double 的输入范围限制为 1 - 5,而且我们已经为这个范围建立的 cache 对象,因此只要参照 cache 就能根据指定输入返回指定输出。也就是一一对应的关系。
那么数学函数的定义是什么呢?
在数学中,函数是一种输入集合和可允许的输出集合之间的关系,具有如下属性:每个输入都精确地关联一个输出。函数的输入称为参数,输出称为值。对于一个给定的函数,所有被允许的输入集合称为该函数的定义域,而被允许的输出集合称为值域。

上面的定义和纯函数完全一致,例如在 double 中,你能找到定义域和值域吗?当然可以!通过这个例子,可以很容易看到数学函数的思想被借鉴到函数式范式的世界

9、 我们要做什么?

我们将通过学习,构建出一个 ES6-Functional 的函数式库,通过构建的过程,我们将理解如何使用 JavaScript 函数,以及如何在日常工作中应用函数式编程。

10、小结

这一节我们只是简单的介绍了函数式编程的概念,以及什么是纯函数,其中最重要的就是引用透明性。然后研究了几个短小的例子,通过例子来加深对函数式编程的理解。接下来我们将一步一步深入了解函数式编程。

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration:
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn