تشبه
أساليب تكرار المصفوفة "بدء تشغيل العقاقير" (بالطبع ، فهي ليست مخدرات ؛ وأنا لا أقول أن العقاقير جيدة ؛ إنها مجرد كلمة في الكلام). بسببهم ، العديد من "الجلوس" على البرمجة الوظيفية. الشيء هو أنها مريحة بشكل لا يصدق. بالإضافة إلى ذلك ، معظم هذه الطرق سهلة الفهم. تقبل أساليب مثل
.map()
و
.filter()
وسيطة رد اتصال واحدة فقط وتتيح لك حل المشكلات البسيطة. ولكن هناك شعور بأن طريقة
.reduce()
تسبب بعض الصعوبات للكثيرين. لفهم ذلك هو أكثر صعوبة قليلا.

لقد
كتبت بالفعل عن سبب اعتقادي.
.reduce()
يخلق الكثير من المشاكل. ويرجع ذلك جزئيًا إلى حقيقة أن العديد من الأدلة تثبت استخدام.
.reduce()
فقط عند التعامل مع الأرقام. لذلك ، كتبت عن عدد المهام التي لا تنطوي على عمليات حسابية يمكن حلها باستخدام
.reduce()
. ولكن ماذا لو كنت بحاجة ماسة للعمل مع الأرقام؟
استخدام نموذجي من
.reduce()
يشبه حساب الوسط الحسابي لعناصر الصفيف. للوهلة الأولى ، يبدو أنه لا يوجد شيء خاص في هذه المهمة. لكنها ليست بهذه البساطة. الحقيقة هي أنه قبل حساب المتوسط ، تحتاج إلى العثور على المؤشرات التالية:
- المبلغ الإجمالي لقيم عنصر الصفيف.
- طول مجموعة.
معرفة كل هذا بسيط جدا. وحساب متوسط القيم للصفائف الرقمية أيضًا ليس عملية صعبة. هنا مثال ابتدائي:
function average(nums) { return nums.reduce((a, b) => (a + b)) / nums.length; }
كما ترون ، لا يوجد أي سوء فهم خاص هنا. لكن المهمة تصبح أكثر صعوبة إذا كان عليك العمل مع هياكل بيانات أكثر تعقيدًا. ماذا لو كان لدينا مجموعة من الأشياء؟ ماذا لو كانت بعض الكائنات من هذه المجموعة بحاجة إلى تصفية؟ ماذا تفعل إذا كنت بحاجة إلى استخراج قيم رقمية معينة من الكائنات؟ في هذه الحالة ، يعد حساب متوسط قيمة عناصر الصفيف مهمة أكثر تعقيدًا بالفعل.
من أجل التعامل مع هذا ، سنحل مشكلة التدريب (يعتمد على
هذه المهمة مع FreeCodeCamp). سنحلها بخمس طرق مختلفة. كل منهم له مزاياه وعيوبه. سيوضح تحليل هذه الطرق الخمسة لحل هذه المشكلة مدى مرونة جافا سكريبت. وآمل أن يعطيك تحليل الحلول غذاءً للتفكير في كيفية استخدام.
.reduce()
في المشاريع الحقيقية.
نظرة عامة على المهمة
افترض أن لدينا مجموعة من الكائنات التي تصف تعبيرات العامية الفيكتورية. تحتاج إلى تصفية تلك التعبيرات غير الموجودة في كتب Google (خاصية
found
على الكائنات المقابلة غير
false
) ، وإيجاد تصنيف متوسط لشعبية التعبيرات. إليك ما قد تبدو عليه هذه البيانات (مأخوذة
من هنا ):
const victorianSlang = [ term: 'doing the bear', found: true, popularity: 108, }, term: 'katterzem', found: false, popularity: null, }, term: 'bone shaker', found: true, popularity: 609, }, term: 'smothering a parrot', found: false, popularity: null, }, term: 'damfino', found: true, popularity: 232, }, term: 'rain napper', found: false, popularity: null, }, term: 'donkey's breakfast', found: true, popularity: 787, }, term: 'rational costume', found: true, popularity: 513, }, term: 'mind the grease', found: true, popularity: 154, }, ];
ضع في اعتبارك 5 طرق للعثور على متوسط قيمة تقييم شعبية التعبيرات من هذه المجموعة.
1. حل مشكلة دون استخدام. تقليل () (حلقة ضرورية)
في الأسلوب الأول الذي نتبعه لحل المشكلة ، لن يتم استخدام طريقة
.reduce()
. إذا لم تكن قد واجهت طرقًا لتكرار المصفوفات من قبل ، فإنني آمل أن يوضح تحليل هذا المثال الموقف لك قليلاً.
let popularitySum = 0; let itemsFound = 0; const len = victorianSlang.length; let item = null; for (let i = 0; i < len; i++) { item = victorianSlang[i]; if (item.found) { popularitySum = item.popularity + popularitySum; itemsFound = itemsFound + 1; } const averagePopularity = popularitySum / itemsFound; console.log("Average popularity:", averagePopularity);
إذا كنت معتادًا على JavaScript ، فسوف تفهم هذا المثال بسهولة. في الواقع ، يحدث ما يلي هنا:
- نحن تهيئة المتغيرات
itemsFound
و itemsFound
. المتغير الأول ، popularitySum
، يخزن التصنيف العام لشعبية التعبيرات. والمتغير الثاني ، itemsFound
، (هذه مفاجأة) يخزن عدد التعبيرات الموجودة. - بعد ذلك ، نقوم بتهيئة
len
الثابت len
المتغير ، وهما أمران مفيدان لنا عند عبور المصفوفة. - في حلقة
for
، تزداد العداد i
حتى تصل قيمتها إلى قيمة مؤشر العنصر الأخير للصفيف. - داخل الحلقة ، نأخذ عنصر الصفيف الذي نريد استكشافه. يمكننا الوصول إلى العنصر باستخدام
victorianSlang[i]
build. - ثم نكتشف ما إذا كان هذا التعبير موجودًا في مجموعة الكتب.
- إذا حدث تعبير في الكتب ، فإننا نأخذ قيمة تصنيف شعبيته ونضيفه إلى قيمة المتغير
popularitySum
. - في الوقت نفسه ، نقوم أيضًا بزيادة عداد التعبيرات الموجودة -
itemsFound
. - وأخيراً ، نجد المتوسط بتقسيم
itemsFound
.
لذلك ، تعاملنا مع المهمة. ربما لم يكن قرارنا جميلًا بشكل خاص ، لكنه يقوم بعمله. إن استخدام طرق للتكرار من خلال المصفوفات سيجعلها أكثر نظافة. دعونا نلقي نظرة على ما إذا كنا ننجح ، والحقيقة هي "تنظيف" هذا القرار.
2. الحل البسيط رقم 1: .filter () و .map () والعثور على الكمية باستخدام .uce ()
دعونا ، قبل المحاولة الأولى لاستخدام أساليب المصفوفات لحل المشكلة ، نقوم بتقسيمها إلى أجزاء صغيرة. وهي هنا ما يتعين علينا القيام به:
- حدد كائنات تمثل التعبيرات الموجودة في مجموعة كتب Google. هنا يمكنك استخدام طريقة
.filter()
. - مقتطف من الكائنات تقييم شعبية التعبيرات. لحل هذه المهمة الفرعية ، تعد طريقة
.map()
مناسبة. - احسب مجموع التصنيفات. هنا يمكننا اللجوء إلى مساعدة صديقنا القديم.
.reduce()
. - وأخيرا ، ابحث عن متوسط قيمة التقديرات.
إليك ما يبدو في الكود:
addScore
نظرة على وظيفة
addScore
، والخط الذي
.reduce()
فيه
.reduce()
. لاحظ أن
addScore
يقبل معلمتين. الأول ،
runningTotal
، يعرف باسم البطارية. يخزن مجموع القيم. تتغير قيمتها في كل مرة نكرر فيها الصفيف وننفذ
return
. المعلمة الثانية ،
popularity
، هي عنصر منفصل في المصفوفة التي نعالجها. في بداية
addScore
على الصفيف ، لم يتم تنفيذ
return
addScore
في
addScore
. هذا يعني أن
runningTotal
لم يتم ضبطه تلقائيًا بعد. لذلك ، عن طريق استدعاء
.reduce()
، نمرر بهذه الطريقة القيمة التي يجب كتابتها
runningTotal
في البداية. هذه هي المعلمة الثانية التي تم تمريرها إلى.
.reduce()
.
لذلك ، قمنا بتطبيق أساليب صفيف التكرار لحل المشكلة. تبين أن الإصدار الجديد من الحل أكثر نظافة من الإصدار السابق. وبعبارة أخرى ، تبين أن القرار كان أكثر تصريحا. نحن لا نخبر جافا سكريبت عن كيفية تنفيذ الحلقة بالضبط ؛ نحن لا نتبع فهارس عناصر المصفوفات. بدلاً من ذلك ، نعلن وظائف المساعد البسيطة ذات الحجم الصغير والجمع بينها. كل العمل الشاق يتم من خلال أساليب الصفيف
.filter()
و
.map()
و
.reduce()
. هذا النهج لحل مثل هذه المشاكل هو أكثر تعبيرا. تعد أساليب المصفوفة هذه أكثر اكتمالًا بكثير مما تستطيع الحلقة القيام به ، فهي تخبرنا عن النية الموضحة في الكود.
3. الحل السهل رقم 2: استخدام بطاريات متعددة
في الإصدار السابق من الحل ، أنشأنا مجموعة كاملة من المتغيرات الوسيطة. على سبيل المثال ،
foundSlangTerms
و
foundSlangTerms
. في حالتنا ، فإن هذا الحل مقبول تمامًا. ولكن ماذا لو وضعنا أنفسنا هدفًا أكثر تعقيدًا فيما يتعلق بتصميم الكود؟ سيكون من الرائع أن نتمكن من استخدام نمط تصميم
واجهة بطلاقة في البرنامج. مع هذا النهج ، سنكون قادرين على سلسلة من المكالمات لجميع الوظائف وتكون قادرة على القيام به دون المتغيرات المتوسطة. ومع ذلك ، هناك مشكلة واحدة تنتظرنا هنا. لاحظ أننا نحتاج إلى الحصول على قيمة
popularityScores.length
. إذا كنا سنرتب كل شيء ، فسنحتاج إلى طريقة أخرى للعثور على عدد العناصر في الصفيف. يلعب عدد العناصر في الصفيف دور المقسوم في حساب القيمة المتوسطة. دعونا نرى ما إذا كان يمكننا تغيير النهج لحل المشكلة بحيث يمكن القيام بكل شيء من خلال الجمع بين استدعاءات الطرق في سلسلة. سنفعل ذلك من خلال تتبع قيمتين عند التكرار على عناصر المصفوفة ، أي باستخدام "البطارية المزدوجة".
هنا ، للعمل مع قيمتين ، استخدمنا الكائن في وظيفة المخفض. كل تمريرة من خلال المصفوفة المنفذة باستخدام
addScrores
، نقوم بتحديث القيمة الإجمالية لتصنيف الشعبية وعدد العناصر. من المهم ملاحظة أن هاتين القيمتين يتم تمثيلهما ككائن مفرد. مع هذا النهج ، يمكننا "خداع" النظام وتخزين كيانين ضمن نفس القيمة المرجعة.
addScrores
وظيفة
addScrores
أكثر تعقيدًا من الوظيفة التي تحمل الاسم نفسه في المثال السابق. ولكن اتضح الآن أنه يمكننا استخدام سلسلة واحدة من مكالمات الطريقة لإجراء جميع العمليات باستخدام الصفيف. نتيجة لمعالجة المصفوفة ، نحصل على كائن
popularityInfo
، الذي يخزن كل ما تحتاجه للعثور على المتوسط. هذا يجعل سلسلة الاتصال نظيفة وبسيطة.
إذا كنت تشعر بالرغبة في تحسين هذا الرمز ، فيمكنك تجربته. على سبيل المثال - يمكنك إعادته للتخلص من العديد من المتغيرات الوسيطة. يمكنك حتى محاولة وضع هذا الرمز في سطر واحد.
4. تكوين وظائف دون استخدام تدوين نقطة
إذا كنت جديدًا في البرمجة الوظيفية ، أو يبدو لك أن البرمجة الوظيفية معقدة للغاية ، يمكنك تخطي هذا القسم. تحليله سيفيدك إذا كنت معتاداً على
curry()
compose()
. إذا كنت ترغب في البحث في هذا الموضوع ، فقم بإلقاء نظرة على
هذه المادة حول البرمجة الوظيفية في JavaScript ، وعلى وجه الخصوص ، في الجزء
الثالث من السلسلة التي يتم تضمينها فيه.
نحن المبرمجون الذين يتخذون نهج وظيفي. هذا يعني أننا نسعى جاهدين لبناء وظائف معقدة من وظائف أخرى - صغيرة وبسيطة. حتى الآن ، أثناء النظر في الخيارات المختلفة لحل المشكلة ، قللنا عدد المتغيرات الوسيطة. نتيجة لذلك ، أصبح رمز الحل أبسط وأسهل. ولكن ماذا لو تم نقل هذه الفكرة إلى أقصى الحدود؟ ماذا لو حاولت التخلص من جميع المتغيرات الوسيطة؟ وحتى محاولة الابتعاد عن بعض المعلمات؟
يمكنك إنشاء دالة لحساب المتوسط باستخدام دالة
compose()
وحدها ، دون استخدام المتغيرات. نحن نسمي هذا "البرمجة دون استخدام الرموز الدقيقة" أو "البرمجة الضمنية". لكتابة مثل هذه البرامج ، ستحتاج إلى العديد من الوظائف الإضافية.
في بعض الأحيان مثل هذا الرمز صدمة الناس. هذا يرجع إلى حقيقة أن مثل هذا النهج يختلف اختلافًا كبيرًا عن النهج المقبول عمومًا. لكنني اكتشفت أن كتابة التعليمات البرمجية في أسلوب البرمجة الضمنية هي واحدة من أسرع الطرق لفهم جوهر البرمجة الوظيفية. لذلك ، يمكنني أن أنصحك بتجربة هذه التقنية في بعض المشاريع الشخصية. لكنني أريد أن أقول أنه ربما يجب ألا تكتب بأسلوب البرمجة الضمنية الشفرة التي يتعين على الآخرين قراءتها.
لذا ، عدنا إلى مهمتنا المتمثلة في بناء نظام لحساب المتوسطات. من أجل توفير مساحة ، سننتقل هنا لاستخدام وظائف السهم. عادة ، كقاعدة عامة ، من الأفضل استخدام الدالات المسماة.
هنا مقال جيد حول هذا الموضوع. يتيح لك ذلك الحصول على نتائج تتبع مكدس أفضل في حالة وجود أخطاء.
إذا بدا كل هذا الكود لك تمامًا - فلا تقلق بشأنه. أدرجتها هنا كممارسة فكرية ، وليس لإزعاجك.
في هذه الحالة ، يكون العمل الرئيسي في وظيفة
compose()
. إذا قمت بقراءة محتوياته من الأسفل إلى الأعلى ، اتضح أن الحسابات تبدأ بتصفية الصفيف حسب خاصية العناصر التي
found
. ثم نسترجع خاصية عنصر
popularity
باستخدام
map()
. بعد ذلك نستخدم ما يسمى "
Combined blackbird combinator ". يتم تمثيل هذا الكيان كدالة
B1
، والتي تُستخدم لإجراء تمريرين للحسابات على مجموعة واحدة من بيانات الإدخال. لفهم هذا بشكل أفضل ، ألق نظرة على هذه الأمثلة:
مرة أخرى ، إذا كنت لا تفهم أي شيء مرة أخرى - لا تقلق. هذا مجرد عرض يوضح أنه يمكن كتابة JavaScript بطرق مختلفة جدًا. من هذه الميزات ، هذا هو جمال هذه اللغة.
5. حل المشكلة في مسار واحد مع حساب القيمة المتوسطة التراكمية
تقوم جميع البرامج المذكورة أعلاه بعمل جيد في حل مشكلتنا (بما في ذلك الدورة الملحة). أولئك الذين يستخدمون طريقة
.reduce()
لديهم شيء مشترك. وهي تستند إلى تقسيم المشكلة إلى أجزاء صغيرة. ثم يتم تجميع هذه الشظايا بطرق مختلفة. من خلال تحليل هذه الحلول ، قد تلاحظ أننا نلتف حولها ثلاث مرات. هناك شعور بأنه غير فعال. سيكون من الرائع إذا كانت هناك طريقة لمعالجة المصفوفة وإرجاع النتيجة في تمريرة واحدة. هذه الطريقة موجودة ، لكن تطبيقها سيتطلب اللجوء إلى الرياضيات.
من أجل حساب متوسط قيمة عناصر الصفيف في مسار واحد ، نحتاج إلى طريقة جديدة. تحتاج إلى العثور على طريقة لحساب المتوسط باستخدام المتوسط المحسوب مسبقًا والقيمة الجديدة. نحن نبحث عن هذه الطريقة باستخدام الجبر.
يمكن العثور على متوسط قيمة الأرقام
n
باستخدام هذه الصيغة:
لمعرفة متوسط
n + 1
، ستفعل الصيغة نفسها ، ولكن في إدخال مختلف:
هذه الصيغة هي نفسها كما يلي:
ونفس الشيء مثل هذا:
إذا قمت بتحويل هذا قليلاً ، فستحصل على ما يلي:
إذا كنت لا ترى هذه النقطة في كل هذا ، فلا بأس بذلك. نتيجة كل هذه التحولات هي أنه بمساعدة الصيغة الأخيرة ، يمكننا حساب متوسط القيمة خلال اجتياز مفرد للصفيف. للقيام بذلك ، تحتاج إلى معرفة قيمة العنصر الحالي ، ومتوسط القيمة المحسوبة في الخطوة السابقة ، وعدد العناصر. بالإضافة إلى ذلك ، يمكن إجراء معظم العمليات الحسابية في وظيفة المخفض:
بفضل هذا النهج ، يمكن العثور على القيمة اللازمة لتجاوز المصفوفة مرة واحدة فقط. تستخدم الأساليب الأخرى مسارًا واحدًا لتصفية الصفيف ، وآخر لاستخراج البيانات اللازمة منه ، وآخر يستخدم للعثور على مجموع قيم العناصر. هنا ، كل شيء يناسب واحد يمر عبر مجموعة.
يرجى ملاحظة أن هذا لا يجعل بالضرورة الحسابات أكثر كفاءة. مع هذا النهج ، يجب إجراء المزيد من العمليات الحسابية. عندما تصل كل قيمة جديدة ، نقوم بإجراء عمليات الضرب والقسمة ، ونقوم بذلك للحفاظ على متوسط القيمة الحالية في الحالة الحالية. في حلول أخرى لهذه المشكلة ، نقسم رقمًا واحدًا إلى آخر مرة واحدة فقط - في نهاية البرنامج. لكن هذا النهج أكثر فعالية من حيث استخدام الذاكرة. لا يتم استخدام المصفوفات الوسيطة هنا ، ونتيجة لذلك يتعين علينا تخزين كائن له قيمتين فقط في الذاكرة.
. . , . . , , .
?
? , . , - . , , , . , . , . , , .
, - , . , ? . . — .
:
.reduce()
..filter()
.map()
, — .reduce()
.- , .
- .
- .
, -, ? — . - — , :
- , . — .
- , , — .
- , , — , .
! JavaScript-?