مقدمة سريعة للأنبوب () والتأليف () في JavaScript

كانت البرمجة الوظيفية هي الرحلة التي تفتح عيناي تمامًا. هذا المنشور ، والمنشورات المشابهة له ، هي محاولة لمشاركة رؤيتي ووجهات نظري بينما أقوم برحلة إلى أراضي البرمجة الوظيفية الجديدة.

كانت Ramda مكتبة go-to 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وإعادته.

We see the result is 'Buckethead' because currentFunction returns the .name property of any object. That will be returned in reduce, meaning it becomes the new currentValue next time. Let’s hit the next debugger and see.

Now currentValue is ‘Buckethead’ because that’s what got returned last time. currentFunction is uppercase, so 'BUCKETHEAD' will be the next currentValue.

The same idea, pluck ‘BUCKETHEAD’'s first 6 characters and hand them off to the next function.

reverse(‘.aedi emaS’)

And you’re done!

What about compose()?

It’s just pipe in the other direction.

So if you wanted the same result as our pipe above, you’d do the opposite.

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

Notice how getName is last in the chain and reverse is first?

إليك تنفيذ سريع compose، مرة أخرى بإذن من Magical Eric Elliott ، من نفس المقالة.

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

سأترك توسيع هذه الوظيفة مع debuggers كتدريب لك. العب بها ، استخدمها ، نقدرها. والأهم من ذلك ، استمتع!