En rask introduksjon til pipe () og compose () i JavaScript
Funksjonell programmering har vært en ganske øyeblikkelig reise for meg. Dette innlegget, og innlegg som det, er et forsøk på å dele min innsikt og perspektiver når jeg trekker nye funksjonelle programmeringsland.
Ramda har vært mitt gå til FP-bibliotek på grunn av hvor mye enklere det gjør funksjonell programmering i JavaScript. Jeg anbefaler det på det sterkeste.
Rør
Konseptet med pipe
er enkelt - det kombinerer n
funksjoner. Det er et rør som flyter fra venstre mot høyre, og kaller hver funksjon med utgangen fra den siste.
La oss skrive en funksjon som returnerer andres name
.
getName = (person) => person.name; getName({ name: 'Buckethead' }); // 'Buckethead'
La oss skrive en funksjon som verserer strenger.
uppercase = (string) => string.toUpperCase(); uppercase('Buckethead'); // 'BUCKETHEAD'
Så hvis vi ønsket å hente og kapitalisere person
navnet, kunne vi gjøre dette:
name = getName({ name: 'Buckethead' }); uppercase(name); // 'BUCKETHEAD'
Det er greit, men la oss eliminere den mellomliggende variabelen name
.
uppercase(getName({ name: 'Buckethead' }));
Bedre, men jeg er ikke glad i det hekkingen. Det kan bli for overfylt. Hva om vi vil legge til en funksjon som får de første 6 tegnene i en streng?
get6Characters = (string) => string.substring(0, 6); get6Characters('Buckethead'); // 'Bucket'
Resulterer i:
get6Characters(uppercase(getName({ name: 'Buckethead' }))); // 'BUCKET';
La oss bli veldig sprø og legge til en funksjon for å snu strengene.
reverse = (string) => string .split('') .reverse() .join(''); reverse('Buckethead'); // 'daehtekcuB'
Nå har vi:
reverse(get6Characters(uppercase(getName({ name: 'Buckethead' })))); // 'TEKCUB'
Det kan bli litt ... mye.
Rør til unnsetning!
I stedet for å fastkjør funksjoner i funksjoner eller lage en mengde mellomvariabler, la oss pipe
alle tingene!
pipe( getName, uppercase, get6Characters, reverse )({ name: 'Buckethead' }); // 'TEKCUB'
Ren kunst. Det er som en todo-liste!
La oss gå gjennom det.
For demoformål vil jeg bruke en pipe
implementering fra en av Eric Elliotts funksjonelle programmeringsartikler.
pipe = (...fns) => (x) => fns.reduce((v, f) => f(v), x);
Jeg elsker denne lille enlinjeren.
Ved hjelp av resten parametere, se min artikkel om at vi kan rør n
funksjoner. Hver funksjon tar utgangen fra den forrige, og er alt redusert ? til en enkelt verdi.
Og du kan bruke den akkurat som vi gjorde ovenfor.
pipe( getName, uppercase, get6Characters, reverse )({ name: 'Buckethead' }); // 'TEKCUB'
Jeg utvider pipe
og legger til noen feilsøkingsuttalelser, og vi går linje for linje.
pipe = (...functions) => (value) => { debugger; return functions.reduce((currentValue, currentFunction) => { debugger; return currentFunction(currentValue); }, value); };
Ring pipe
med vårt eksempel og la underverkene utfolde seg.
Sjekk ut de lokale variablene. functions
er en matrise av de fire funksjonene, og value
er { name: 'Buckethead' }
.
Siden vi brukte resten parametre, pipe
kan en rekke funksjoner som skal brukes. Det vil bare løkke og ringe hver enkelt.
På neste feilsøkingsprogram er vi inne reduce
. Dette er hvor currentValue
blir sendt til currentFunction
og returnert.
Vi ser at resultatet er 'Buckethead'
fordi currentFunction
returnerer .name
eiendommen til ethvert objekt. Det vil bli returnert reduce
, noe som betyr at det blir nytt currentValue
neste gang. La oss slå på neste feilsøkingsprogram og se.
Nå currentValue
er ‘Buckethead’
fordi det er det som ble returnert sist gang. currentFunction
er uppercase
, så 'BUCKETHEAD'
blir det neste currentValue
.
Den samme ideen, plukk ‘BUCKETHEAD’
de første 6 tegnene og gi dem videre til neste funksjon.
reverse(‘.aedi emaS’)
Og du er ferdig!
Hva med å komponere ()?
Det er bare pipe
i den andre retningen.
Så hvis du ønsket det samme resultatet som pipe
ovenfor, ville du gjøre det motsatte.
compose( reverse, get6Characters, uppercase, getName )({ name: 'Buckethead' });
Legg merke til hvordan getName
er sist i kjeden og reverse
er først?
Her er en rask implementering av compose
, igjen med tillatelse fra Magical Eric Elliott, fra samme artikkel.
compose = (...fns) => (x) => fns.reduceRight((v, f) => f(v), x);
Jeg lar deg utvide denne funksjonen med debugger
s som en øvelse for deg. Lek med det, bruk det, setter pris på det. Og viktigst av alt, ha det gøy!