Geheimnisse der JavaScript-Küche: Gewürze

Schauen Sie sich die folgenden Codefragmente an, die das gleiche Problem lösen, und überlegen Sie, welches Ihnen am besten gefällt.
Hier ist der erste:Hier ist der zweite:
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10] .filter(int => isEven(int)) .filter(int => isBiggerThan(3, int)) .map(int => int + 1) .map(int => toChar(int)) .filter(char => !isVowel(char)) .join('') // 'fhjl' 
 [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] .filter(isEven) .filter(isBiggerThan(3)) .map(plus(1)) .map(toChar) .filter(not(isVowel)) .join('') // 'fhjl' 
"Ich wette, dass die zweite Option viel besser lesbar ist als die erste", sagt der Autor des Materials, dessen Übersetzung wir heute veröffentlichen. Ihm zufolge liegt der springende Punkt in den Argumenten der Methoden filter() und map() .



Heute werden wir darüber sprechen, wie Code ähnlich wie im ersten Beispiel so recycelt wird, dass er dem Code aus dem zweiten Beispiel ähnelt. Der Autor des Artikels verspricht, dass Sie, nachdem Sie verstanden haben, wie es funktioniert, eine neue Beziehung zu Ihren Programmen herstellen und nicht ignorieren können, was früher ganz normal erschien und nicht verbessert werden muss.

Einfache Funktion


Stellen Sie sich eine einfache sum() Funktion vor, die die übergebenen Zahlen addiert:

 const sum = (a, b) => a + b sum(1, 2) // 3 

Wir schreiben es neu und geben der neuen Funktion den Namen csum() :

 const csum = a => b => a + b csum(1)(2) // 3 

Die neue Version funktioniert genauso wie die ursprüngliche. Der einzige Unterschied besteht darin, wie diese neue Funktion aufgerufen wird. Die Funktion sum() csum() nämlich zwei Parameter gleichzeitig, und csum() dieselben Parameter. Tatsächlich werden beim Aufrufen von csum() zwei Funktionen aufgerufen. Betrachten Sie insbesondere die Situation, in der csum() aufgerufen wird, und übergeben Sie die Nummer 1 und sonst nichts:

 csum(1) // b => 1 + b 

Ein solcher Aufruf von csum() führt dazu, dass eine Funktion zurückgegeben wird, die das zweite numerische Argument akzeptieren kann, das während des üblichen Aufrufs an csum() , und das Ergebnis des Hinzufügens eines Arguments zu diesem Argument zurückgibt. Rufen Sie diese Funktion plusOne() :

 const plusOne = csum(1) plusOne(2) // 3 

Arbeiten Sie mit Arrays


In JavaScript können Sie mit Arrays mit einer Vielzahl spezieller Methoden arbeiten. Angenommen, die map() -Methode wird verwendet, um die an sie übergebene Funktion auf jedes Element des Arrays anzuwenden.

Um beispielsweise jedes Element eines ganzzahligen Arrays um 1 zu erhöhen (genauer gesagt, um ein neues Array zu bilden, das Elemente des ursprünglichen Arrays enthält, die um 1 erhöht wurden), können Sie die folgende Konstruktion verwenden:

 [1, 2, 3].map(x => x + 1) // [2, 3, 4] 

Mit anderen Worten, was passiert, kann wie folgt beschrieben werden: Die Funktion x => x + 1 nimmt eine ganze Zahl und gibt die darauf folgende Zahl in einer Reihe von ganzen Zahlen zurück. Mit der plusOne() Funktion plusOne() kann dieses Beispiel wie folgt umgeschrieben werden:

 [1, 2, 3].map(x => plusOne(x)) // [2, 3, 4] 

Hier lohnt es sich, langsamer zu fahren und darüber nachzudenken, was passiert. Wenn Sie dies tun, können Sie sehen, dass im betrachteten Fall die Konstruktionen x => plusOne(x) und plusOne (beachten Sie, dass in dieser Situation keine Klammern nach dem Funktionsnamen stehen) äquivalent sind. Um dies besser zu verstehen, betrachten Sie die Funktion otherPlusOne() :

 const otherPlusOne = x => plusOne(x) otherPlusOne(1) // 2 

Das Ergebnis dieser Funktion ist das gleiche wie das, das durch einen einfachen Aufruf von plusOne() , das uns bereits bekannt ist:

 plusOne(1) // 2 

Aus dem gleichen Grund können wir über die Äquivalenz der folgenden beiden Konstruktionen sprechen. Hier ist der erste, den wir bereits gesehen haben:

 [1, 2, 3].map(x => plusOne(x)) // [2, 3, 4] 

Hier ist der zweite:

 [1, 2, 3].map(plusOne) // [2, 3, 4] 

plusOne() außerdem daran, wie die Funktion plusOne() erstellt wurde:

 const plusOne = csum(1) 

Dies ermöglicht es uns, unsere Konstruktion mit map() wie folgt umzuschreiben:

 [1, 2, 3].map(csum(1)) // [2, 3, 4] 

isBiggerThan() nun mit derselben Technik isBiggerThan() Funktion isBiggerThan() . Wenn Sie möchten, versuchen Sie es selbst und lesen Sie dann weiter. Dadurch wird die Verwendung unnötiger Konstruktionen bei Verwendung der filter() -Methode vermieden. Lassen Sie uns zuerst den Code in dieses Formular bringen:

 const isBiggerThan = (threshold, int) => int > threshold [1, 2, 3, 4].filter(int => isBiggerThan(3, int)) 

Wenn wir dann alles Überflüssige loswerden, erhalten wir den Code, den Sie bereits ganz am Anfang dieses Materials gesehen haben:

 [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] .filter(isEven) .filter(isBiggerThan(3)) .map(plus(1)) .map(toChar) .filter(not(isVowel)) .join('') // 'fhjl' 

Wir betrachten nun zwei einfache Regeln, mit denen Sie Code in dem hier beschriebenen Stil schreiben können.

Regel Nummer 1


Die folgenden zwei Konstruktionen sind äquivalent:

 […].map(x => fnc(x)) […].map(fnc) 

Regel Nummer 2


Rückrufe können jederzeit neu geschrieben werden, um die Anzahl der zum Aufrufen verwendeten Argumente zu verringern:

 const fnc = (x, y, z) => … […].map(x => fnc(x, y, z)) const fnc = (y, z) => x => … […].map(fnc(y, z)) 

Wenn Sie selbst die Funktion isBiggerThan() , haben Sie wahrscheinlich bereits auf eine solche Transformation zurückgegriffen. Angenommen, wir benötigen Zahlen größer als 3, um einen Filter zu passieren. Dies kann folgendermaßen geschehen:

 const isBiggerThan = (threshold, int) => int > threshold […].filter(int => isBiggerThan(3, int)) 

Jetzt schreiben wir die Funktion isBiggerThan() , damit sie in der filter() -Methode verwendet werden kann und nicht das Konstrukt int=> :

 const isBiggerThan = threshold => int => int > threshold […].map(isBiggerThan(3)) 

Übung


Angenommen, wir haben das folgende Codefragment:

 const keepGreatestChar = (char1, char2) => char1 > char2 ? char1 : char2 keepGreatestChar('b', 'f') // 'f' //   'f'   'b' 

Erstellen keepGreatestCharBetweenBAnd() nun basierend auf der Funktion keepGreatestCharBetweenBAnd() Funktion keepGreatestCharBetweenBAnd() . Wir brauchen das, indem wir es nennen, können wir ihm nur ein Argument übergeben, während es das ihm übergebene Zeichen mit dem Zeichen b . Diese Funktion kann folgendermaßen aussehen:

 const keepGreatestChar = (char1, char2) => char1 > char2 ? char1 : char2 const keepGreatestCharBetweenBAnd = char => keepGreatestChar('b', char) keepGreatestCharBetweenBAnd('a') // 'b' //   'b'   'a' 

Schreiben Sie nun die Funktion keepGreatestChar() , mit der keepGreatestChar() mithilfe der Funktion keepGreatestChar() in der Array-Methode keepGreatestChar() nach dem "größten" Zeichen suchen können und keine Argumente benötigen. Beginnen wir mit diesem Code:

 const keepGreatestChar = (char1, char2) => char1 > char2 ? char1 : char2 const greatestCharInArray = array => array.reduce((acc, char) => acc > char ? acc : char, 'a') greatestCharInArray(['a', 'b', 'c', 'd']) // 'd' 

Um dieses Problem zu lösen, implementieren Sie die Funktion creduce() , die in der Funktion greatestCharInArray() verwendet werden kann. Mit dieser Funktion können Sie in der praktischen Anwendung dieser Funktion nur das Array übergeben, in dem das Zeichen mit dem größten Code gefunden werden soll.

Die Funktion creduce() muss universell genug sein, damit sie zur Lösung aller Probleme verwendet werden kann, bei denen die Funktionen der Standardmethode creduce() werden müssen. Mit anderen Worten, die Funktion muss einen Rückruf, einen Anfangswert und ein Array annehmen, um damit arbeiten zu können. Als Ergebnis sollten Sie eine Funktion erhalten, mit der das folgende Codefragment funktioniert:

 const greatestCharInArray = creduce(keepGreatestChar, 'a') greatestCharInArray(['a', 'b', 'c', 'd']) // 'd' 

Zusammenfassung


Vielleicht haben Sie jetzt eine Frage, warum Methoden, die gemäß der hier vorgestellten Methodik verarbeitet wurden, Namen haben, die mit dem Zeichen c . Das Zeichen c ist eine Abkürzung für Curry, und wir haben darüber gesprochen, wie Curry-Funktionen zur Verbesserung der Lesbarkeit von Code beitragen. Es ist anzumerken, dass wir hier nicht die strikte Einhaltung der Prinzipien der funktionalen Programmierung angestrebt haben, aber wir glauben, dass die praktische Anwendung dessen, was hier diskutiert wurde, es uns ermöglicht, den Code zu verbessern. Wenn das Thema Currying in JavaScript für Sie interessant ist, wird empfohlen, das 4. Kapitel dieses Buches über funktionale Programmierung zu lesen und im Allgemeinen, da Sie diesen Punkt erreicht haben, das gesamte Buch zu lesen. Wenn Sie mit der funktionalen Programmierung noch nicht vertraut sind, lesen Sie dieses Startermaterial.

Liebe Leser! Verwenden Sie Funktionscurrying in der JavaScript-Entwicklung?

Source: https://habr.com/ru/post/de415059/


All Articles