Μια γρήγορη εισαγωγή στο σωληνάριο () και τη σύνταξη () σε JavaScript
Ο λειτουργικός προγραμματισμός ήταν ένα αρκετά ανοιχτό ταξίδι για μένα. Αυτή η ανάρτηση, και δημοσιεύσεις σαν αυτήν, είναι μια προσπάθεια να μοιραστώ τις ιδέες και τις προοπτικές μου καθώς ταξιδεύω σε νέα λειτουργικά προγράμματα.
Η Ramda ήταν η βιβλιοθήκη FP μου, γιατί είναι πολύ πιο εύκολο να κάνει λειτουργικό προγραμματισμό σε JavaScript. Το συνιστώ ανεπιφύλακτα.
Σωλήνας
Η έννοια του pipe
είναι απλή - συνδυάζει n
λειτουργίες. Είναι ένας σωλήνας που ρέει αριστερά προς τα δεξιά, καλεί κάθε λειτουργία με την έξοδο της τελευταίας.
Ας γράψουμε μια συνάρτηση που επιστρέφει κάποιον name
.
getName = (person) => person.name; getName({ name: 'Buckethead' }); // 'Buckethead'
Ας γράψουμε μια συνάρτηση που είναι κεφαλαία.
uppercase = (string) => string.toUpperCase(); uppercase('Buckethead'); // 'BUCKETHEAD'
Έτσι, αν θέλαμε να πάρουμε και να κεφαλαιοποιήσουμε person
το όνομα, θα μπορούσαμε να το κάνουμε:
name = getName({ name: 'Buckethead' }); uppercase(name); // 'BUCKETHEAD'
Αυτό είναι εντάξει, αλλά ας εξαλείψουμε αυτήν την ενδιάμεση μεταβλητή name
.
uppercase(getName({ name: 'Buckethead' }));
Καλύτερα, αλλά δεν μου αρέσει αυτή η φωλιά. Μπορεί να είναι πολύ γεμάτο. Τι γίνεται αν θέλουμε να προσθέσουμε μια συνάρτηση που λαμβάνει τους πρώτους 6 χαρακτήρες μιας συμβολοσειράς;
get6Characters = (string) => string.substring(0, 6); get6Characters('Buckethead'); // 'Bucket'
Εχοντας ως αποτέλεσμα:
get6Characters(uppercase(getName({ name: 'Buckethead' }))); // 'BUCKET';
Ας πάμε πραγματικά τρελοί και προσθέστε μια λειτουργία για να αντιστρέψετε τις χορδές.
reverse = (string) => string .split('') .reverse() .join(''); reverse('Buckethead'); // 'daehtekcuB'
Τώρα έχουμε:
reverse(get6Characters(uppercase(getName({ name: 'Buckethead' })))); // 'TEKCUB'
Μπορεί να πάρει λίγο… πολύ.
Σωλήνας για τη διάσωση!
Αντί να μπλοκάρει συναρτήσεις μέσα σε συναρτήσεις ή να δημιουργεί μια δέσμη ενδιάμεσων μεταβλητών, ας κάνουμε pipe
όλα!
pipe( getName, uppercase, get6Characters, reverse )({ name: 'Buckethead' }); // 'TEKCUB'
Καθαρή τέχνη. Είναι σαν μια λίστα υποχρεώσεων!
Ας το περάσουμε.
Για σκοπούς επίδειξης, θα χρησιμοποιήσω μια pipe
εφαρμογή από ένα από τα άρθρα λειτουργικού προγραμματισμού του Eric Elliott.
pipe = (...fns) => (x) => fns.reduce((v, f) => f(v), x);
Λατρεύω αυτό το μικρό σκάφος.
Χρησιμοποιώντας παραμέτρους ανάπαυσης , δείτε το άρθρο μου σχετικά με αυτό, μπορούμε να κάνουμε σωληνώσεις n
. Κάθε συνάρτηση λαμβάνει την έξοδο της προηγούμενης και μειώνεται ; σε μία τιμή.
Και μπορείτε να το χρησιμοποιήσετε όπως κάναμε παραπάνω.
pipe( getName, uppercase, get6Characters, reverse )({ name: 'Buckethead' }); // 'TEKCUB'
Θα επεκτείνω pipe
και θα προσθέσω κάποιες δηλώσεις εντοπισμού σφαλμάτων και θα ακολουθούμε κάθε γραμμή
pipe = (...functions) => (value) => { debugger; return functions.reduce((currentValue, currentFunction) => { debugger; return currentFunction(currentValue); }, value); };
Καλέστε pipe
με το παράδειγμά μας και αφήστε τα θαύματα να ξεδιπλωθούν.
Ελέγξτε τις τοπικές μεταβλητές. functions
είναι ένας πίνακας από τις 4 συναρτήσεις και value
είναι { name: 'Buckethead' }
.
Εφόσον χρησιμοποιήσαμε παραμέτρους ανάπαυσης , pipe
επιτρέπει τη χρήση οποιουδήποτε αριθμού λειτουργιών. Θα βγει απλώς και θα καλέσει το καθένα.
Στο επόμενο πρόγραμμα εντοπισμού σφαλμάτων, είμαστε μέσα reduce
. Αυτό είναι το σημείο currentValue
που μεταφέρεται currentFunction
και επιστρέφεται.
Βλέπουμε το αποτέλεσμα 'Buckethead'
γιατί currentFunction
επιστρέφει την .name
ιδιότητα οποιουδήποτε αντικειμένου. Αυτό θα επιστραφεί reduce
, που σημαίνει ότι θα γίνει το νέο την currentValue
επόμενη φορά. Ας πατήσουμε το επόμενο πρόγραμμα εντοπισμού σφαλμάτων και να δούμε.
Τώρα currentValue
είναι ‘Buckethead’
γιατί αυτό επέστρεψε την τελευταία φορά. currentFunction
είναι uppercase
, έτσι 'BUCKETHEAD'
θα είναι το επόμενο currentValue
.
Η ίδια ιδέα, αποσπάστε ‘BUCKETHEAD’
τους πρώτους 6 χαρακτήρες και παραδώστε τους στην επόμενη λειτουργία.
reverse(‘.aedi emaS’)
Και τελειώσατε!
Τι γίνεται με τη σύνθεση ();
Είναι ακριβώς pipe
προς την άλλη κατεύθυνση.
Αν θέλετε το ίδιο αποτέλεσμα με τα pipe
παραπάνω, θα κάνατε το αντίθετο.
compose( reverse, get6Characters, uppercase, getName )({ name: 'Buckethead' });
Παρατηρήστε πώς getName
είναι το τελευταίο στην αλυσίδα και reverse
είναι το πρώτο;
Ακολουθεί μια γρήγορη εφαρμογή compose
, για άλλη μια φορά, του Magical Eric Elliott, από το ίδιο άρθρο.
compose = (...fns) => (x) => fns.reduceRight((v, f) => f(v), x);
Θα αφήσω να επεκτείνω αυτήν τη λειτουργία με το debugger
s ως άσκηση σε εσάς. Παίξτε μαζί του, χρησιμοποιήστε το, εκτιμήστε το. Και το πιο σημαντικό, διασκεδάστε!