Eine kurze Einführung in pipe () und compose () in JavaScript

Funktionale Programmierung hat mir die Augen geöffnet. Dieser und ähnliche Beiträge sind ein Versuch, meine Einsichten und Perspektiven zu teilen, während ich neue funktionale Programmierländer erkunde.

Ramda war meine bevorzugte FP-Bibliothek, da es die funktionale Programmierung in JavaScript erheblich vereinfacht. Ich empfehle es sehr.

Rohr

Das Konzept von pipeist einfach - es kombiniert nFunktionen. Es ist eine Pipe, die von links nach rechts fließt und jede Funktion mit der Ausgabe der letzten aufruft.

Schreiben wir eine Funktion, die jemandes zurückgibt name.

getName = (person) => person.name; getName({ name: 'Buckethead' }); // 'Buckethead' 

Schreiben wir eine Funktion, die Zeichenfolgen in Großbuchstaben schreibt.

uppercase = (string) => string.toUpperCase(); uppercase('Buckethead'); // 'BUCKETHEAD' 

Wenn wir also den Namen erhalten und groß schreiben wollten person, könnten wir dies tun:

name = getName({ name: 'Buckethead' }); uppercase(name); // 'BUCKETHEAD' 

Das ist in Ordnung, aber lassen Sie uns diese Zwischenvariable eliminieren name.

uppercase(getName({ name: 'Buckethead' })); 

Besser, aber ich mag diese Verschachtelung nicht. Es kann zu voll werden. Was ist, wenn wir eine Funktion hinzufügen möchten, die die ersten 6 Zeichen einer Zeichenfolge erhält?

get6Characters = (string) => string.substring(0, 6); get6Characters('Buckethead'); // 'Bucket' 

Ergebend:

get6Characters(uppercase(getName({ name: 'Buckethead' }))); // 'BUCKET'; 

Lassen Sie uns wirklich verrückt werden und eine Funktion zum Umkehren von Zeichenfolgen hinzufügen.

reverse = (string) => string .split('') .reverse() .join(''); reverse('Buckethead'); // 'daehtekcuB' 

Jetzt haben wir:

reverse(get6Characters(uppercase(getName({ name: 'Buckethead' })))); // 'TEKCUB' 

Es kann ein bisschen ... viel werden.

Rohr zur Rettung!

Anstatt Funktionen innerhalb von Funktionen zu blockieren oder eine Reihe von Zwischenvariablen zu erstellen, lassen Sie uns pipealle Dinge tun!

pipe( getName, uppercase, get6Characters, reverse )({ name: 'Buckethead' }); // 'TEKCUB' 

Kunst pur. Es ist wie eine Aufgabenliste!

Lassen Sie uns durchgehen.

Für Demozwecke verwende ich eine pipeImplementierung aus einem der funktionalen Programmierartikel von Eric Elliott.

pipe = (...fns) => (x) => fns.reduce((v, f) => f(v), x); 

Ich liebe diesen kleinen Einzeiler.

Mit Ruhe Parameter finden Sie in meinem Artikel über das, können wir Rohr nFunktionen. Jede Funktion nimmt die Ausgabe der vorherigen und es ist alles reduziert ? auf einen einzigen Wert.

Und Sie können es genauso verwenden, wie wir es oben getan haben.

pipe( getName, uppercase, get6Characters, reverse )({ name: 'Buckethead' }); // 'TEKCUB' 

Ich werde pipeeinige Debugger-Anweisungen erweitern und hinzufügen, und wir werden Zeile für Zeile vorgehen.

pipe = (...functions) => (value) => { debugger; return functions.reduce((currentValue, currentFunction) => { debugger; return currentFunction(currentValue); }, value); }; 

Rufen Sie pipemit unserem Beispiel an und lassen Sie die Wunder sich entfalten.

Überprüfen Sie die lokalen Variablen. functionsist ein Array der 4 Funktionen und valueist { name: 'Buckethead' }.

Da wir verwenden Rest Parameter pipeermöglicht eine beliebige Anzahl von Funktionen verwendet werden. Es wird einfach wiederholt und aufgerufen.

Beim nächsten Debugger sind wir drinnen reduce. Hier wird currentValueweitergegeben currentFunctionund zurückgegeben.

Wir sehen, dass das Ergebnis darin besteht, 'Buckethead'dass currentFunctiondie .nameEigenschaft eines Objekts zurückgegeben wird. Das wird zurückgegeben reduce, was bedeutet, dass es das currentValuenächste Mal das neue wird. Lassen Sie uns den nächsten Debugger starten und sehen.

Das currentValueliegt ‘Buckethead’daran, dass das letzte Mal zurückgegeben wurde. currentFunctionist uppercase, so 'BUCKETHEAD'wird der nächste sein currentValue.

Die gleiche Idee: Zupfen Sie ‘BUCKETHEAD’die ersten 6 Zeichen und geben Sie sie an die nächste Funktion weiter.

reverse(‘.aedi emaS’)

Und du bist fertig!

Was ist mit compose ()?

Es ist nur pipein die andere Richtung.

Wenn Sie also das gleiche Ergebnis wie pipeoben erzielen möchten, würden Sie das Gegenteil tun.

compose( reverse, get6Characters, uppercase, getName )({ name: 'Buckethead' }); 

Beachten Sie, wie getNameist zuletzt in der Kette und reverseist zuerst?

Hier ist eine schnelle Implementierung compose, ebenfalls mit freundlicher Genehmigung von Magical Eric Elliott, aus demselben Artikel.

compose = (...fns) => (x) => fns.reduceRight((v, f) => f(v), x); 

Ich überlasse es Ihnen, diese Funktion mit debuggers als Übung zu erweitern. Spielen Sie damit herum, benutzen Sie es, schätzen Sie es. Und vor allem viel Spaß!