Heim > Artikel > Web-Frontend > Einführung und Zusammenfassung der funktionalen Programmierung (mit Code)
Vor kurzem habe ich die Einführungsklassiker der funktionalen Programmierung gelesen und sie selbst zusammengefasst. Wenn Sie interessiert sind oder sie benötigen, können Sie einen Blick darauf werfen.
1. Was ist funktionale Programmierung?
Funktionale Programmierung basiert hauptsächlich auf mathematischen Funktionen und ihren Ideen. Schauen wir uns also zunächst die Funktionen in der Mathematik an.
y = f (x)
Das heißt, die Funktion f(x) benötigt x als Parameter und y als Ergebnis. x und y können beliebige Zahlen sein, was mehrere Schlüsselpunkte enthält:
1 Parameter
2. Funktionen müssen immer einen Wert zurückgeben
3. Funktionen sollten basierend auf den Parametern ausgeführt werden, die sie erhalten (z. B. x), nicht auf der externen Umgebung
4. Für ein gegebenes x wird nur der ausgegeben unique y
Lassen Sie es uns anhand eines Beispiels verstehen
// 一个计税函数 var percentValue = 5; var calculateTax = (value) => {return value/100 * (100 + percentValue)};
Verwenden Sie Funktionen in der Mathematik, um diese Funktion zu analysieren. Schauen Sie sich zunächst den dritten Punkt an. Die Funktion sollte auf den empfangenen Parametern und nicht auf der externen Umgebung basieren. Die Funktion berechne Steuer basiert hier auf dem externen Prozentwert. Daher kann diese Funktion nicht als Funktion im mathematischen Sinne bezeichnet werden. Wie können wir sie also in eine Funktion im mathematischen Sinne umwandeln? Ganz einfach
var berechneTax = (Wert, Prozentwert) => {Rückgabewert/100 * (100 + Prozentwert)};
Jetzt kann diese Funktion als echte Funktion bezeichnet werden.
Lassen Sie uns nun funktionale Programmierung in einfachen technischen Begriffen definieren: Funktionale Programmierung ist ein Paradigma, bei dem eine Funktion ihre eigene Logik vervollständigen kann, indem sie sich nur auf Eingabe und Ausgabe verlässt. Dadurch wird sichergestellt, dass die Funktion bei mehrmaligem Aufruf das gleiche Ergebnis zurückgibt. Die Funktion verändert keine externen Umgebungsvariablen.
2. Referenztransparenz
Gemäß der Definition der Funktion können wir schließen, dass alle Funktionen den gleichen Wert für die gleiche Eingabe und Ausgabe zurückgeben. Diese Eigenschaft von Funktionen wird als referenzielle Transparenz bezeichnet
Als Beispiel
var identity = (i) => { return i };
Wir definieren eine einfache Funktion, die nicht auf globale Variablen innerhalb der Funktion angewiesen ist, sondern einfach die Eingabe zurückgibt. Angenommen, es wird zwischen anderen Funktionsaufrufen wie
sum(4,5) + identity(1);
angewendet. Gemäß der Definition der referenziellen Transparenz können wir es in
sum(4,5) + 1;
konvertieren. Dieser Vorgang wird als Ersatzmodell bezeichnet, sodass Sie das Ergebnis erhalten Die Funktion kann direkt ersetzt werden (hauptsächlich, weil die Logik der Funktion nicht von anderen globalen Variablen abhängt).
Da die Funktion für ein bestimmtes Eingabeergebnis denselben Wert zurückgibt, können wir ihn tatsächlich zwischenspeichern. Beispielsweise haben wir eine Funktion „Fakultät“, um die Fakultät zu berechnen. Zur Berechnung seiner Fakultät ist ein Argument erforderlich, beispielsweise ist die Fakultät von 5 120. Wenn der Benutzer die Fakultät 5 zum zweiten Mal eingibt, wissen wir aufgrund der Referenztransparenz (dasselbe Ergebnis wird für dieselbe Eingabe zurückgegeben), dass das Ergebnis 120 ist, aber die Maschine weiß es nicht. Wir müssen das mitteilen Das Ergebnis wird maschinell zwischengespeichert, sodass zukünftige Aufrufe das Ergebnis direkt zurückgeben können, anstatt es erneut berechnen zu müssen. Dies zeigt die Bedeutung referenzieller Transparenz und zwischenspeicherbarem Code in der funktionalen Programmierung.
3. Funktional, deklarativ und abstrakt
Funktionale Programmierung befürwortet deklarative Programmierung und das Schreiben von abstraktem Code
Was ist deklarative Programmierung?
Angenommen, wir möchten ausdrucken Um alle Elemente in einem Array anzuzeigen, können wir die folgende Methode verwenden:
var array = [1,2,3]; for(let i = 0; i < array.length; i++){ console.log(array[i]) }
In diesem Code sagen wir dem Code genau, wie es geht. Zum Beispiel: Ermitteln der Länge des Arrays, Schleifen des Arrays und Ermitteln jedes Elements anhand des Index. Das ist zwingende Programmierung. Befürworter der imperativen Programmierung sagen dem Compiler, was er tun soll.
Sehen wir uns einen anderen Weg an
var arr = [1,2,3]; arr.forEach((ele) => { console.log(ele) })
Im obigen Code haben wir das Abrufen der Array-Länge, das Schleifen des Arrays, die Verwendung des Index zum Abrufen der Array-Elemente usw. entfernt. Wir kümmern uns nur darum, was zu tun ist (z. B. das Drucken von Array-Elementen), die Ermittlung der Array-Länge, Schleifen usw. werden von der Maschine erledigt. Wir müssen uns nur darum kümmern, was zu tun ist, nicht wie deklarative Programmierung
Funktionale Programmierung Die Programmierung befürwortet die abstrakte Erstellung von Funktionen, die an anderer Stelle im Code wiederverwendet werden können.
4. Reine Funktion
Was ist eine reine Funktion? Eine reine Funktionsfunktion gibt für eine bestimmte Eingabe die gleiche Ausgabe zurück. Das obige Double
var double = (value) => value * 2;
ist beispielsweise eine reine Funktion, da es für die gleiche Eingabe immer die gleiche Ausgabe zurückgibt. Reine Funktionen gehorchen der referenziellen Transparenz, daher können wir double(5) einfach durch 10 ersetzen. Was ist das Coole an reinen Funktionen? Werfen wir einen Blick auf
1.4.1 Reine Funktionen erzeugen testbaren Code
Unreine Funktionen haben Nebenwirkungen als Beispiel, um diese Funktion zu veranschaulichen
var percentValue = 5; var calculateTax = (value) => {return value/100 * (100 + percentValue)};
Es ist keine reine Funktion, hauptsächlich weil sie zur Berechnung ihrer Logik von der externen Umgebung abhängt. Wenn sich die externe Umgebung ändert, wirkt sich dies auf das Ergebnis aus. Daher besteht das Hauptmerkmal einer reinen Funktion darin, dass sie nicht von externen Variablen abhängt und keine externen Variablen ändern sollte. Wenn Sie externe Variablen ändern, kann dies zu Änderungen im Verhalten anderer Funktionen führen, also zu Nebenwirkungen, die das Verhalten des Systems unvorhersehbar machen können.
1.4.2 Angemessener Code
Wir sollten die Funktion der Funktion anhand ihres Namens ableiten, z. B. Doppelfunktion
var double = (value) => value * 2;
我们可以通过函数名轻易的推出这个函数会把给定的数值加倍,因此根据引用透明性,我们可以直接把 double(5) 替换成 10。还有一个例子,Math.max(3,4,5,6) 结果是什么?虽然我们只看到了函数的名字,但是我们很容易看出结果,我们看到实现了吗?并没有,为什么,就是因为 Math.max 是纯函数啊!!!
5、 并发代码
纯函数允许我们并发的执行代码,因为纯函数不会改变它的环境,这意味着我们根本不需要担心同步问题。当然,js 是单线程的,但是如果项目中使用了 webworker 来并发执行任务,该怎么办?或者有一段 Node 环境中的服务端代码需要并发的执行函数,又该怎么办呢?
// 非纯函数代码 let global = 'something' 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('first time') } 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('first time') } 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、小结
这一节我们只是简单的介绍了函数式编程的概念,以及什么是纯函数,其中最重要的就是引用透明性。然后研究了几个短小的例子,通过例子来加深对函数式编程的理解。接下来我们将一步一步深入了解函数式编程。
Das obige ist der detaillierte Inhalt vonEinführung und Zusammenfassung der funktionalen Programmierung (mit Code). Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!