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('') |
"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)
Wir schreiben es neu und geben der neuen Funktion den Namen
csum()
:
const csum = a => b => a + b csum(1)(2)
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)
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)
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)
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))
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)
Das Ergebnis dieser Funktion ist das gleiche wie das, das durch einen einfachen Aufruf von
plusOne()
, das uns bereits bekannt ist:
plusOne(1)
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))
Hier ist der zweite:
[1, 2, 3].map(plusOne)
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))
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('')
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')
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'])
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'])
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?
