Fold أم Reduce في Kotlin: السؤال الأبدي للمبتدئين
\n\nإذا كنت تكتب بلغة Kotlin لمدة أسبوع على الأقل، فمن المحتمل أنك قد صادفت الدالتين fold و reduce. للوهلة الأولى، تقومان بنفس الشيء: تمران على المجموعة ويجمعان النتيجة. لكن الشيطان، كما هو الحال دائمًا، يكمن في التفاصيل. الاختيار الخاطئ قد يؤدي إلى أخطاء برمجية، استثناءات، أو ببساطة إلى كود غير أنيق.
في هذه المقالة، سنحلل الفرق بين fold و reduce في Kotlin، وننظر إلى أمثلة حية من مشاريع حقيقية، ونتذكر إلى الأبد متى نستخدم أي دالة. هيا بنا!
\n\nما هما fold و reduce؟ فهم أساسي
\n\nكلتا الدالتين تنتميان إلى العمليات النهائية للمجموعات. تأخذان جميع العناصر وتجمعهما في قيمة واحدة. تخيل أن لديك كومة من الفطائر وتريد الحصول على برغر واحد كبير. fold و reduce هما طريقتان مختلفتان للطي.
Reduce: نهج بسيط
\n\nتستخدم الدالة reduce العنصر الأول من المجموعة كقيمة أولية للمراكم. هذا يعني أنها لا تستطيع العمل مع مجموعة فارغة - سيتم إلقاء UnsupportedOperationException.
val numbers = listOf(1, 2, 3, 4, 5)\nval sum = numbers.reduce { acc, i -> acc + i }\nprintln(sum) // 15\n\nهنا، acc يساوي في البداية 1 (العنصر الأول)، ثم يضاف إليه 2، ثم 3، وهكذا. الأمر بسيط وأنيق، لكن هناك فارق بسيط.
Fold: التحكم في البداية
\n\nعلى عكس reduce، تتطلب fold تحديد قيمة أولية بشكل صريح. هذا يمنحك تحكمًا كاملاً، والأهم من ذلك، يسمح لك بالعمل مع المجموعات الفارغة.
val numbers = listOf(1, 2, 3, 4, 5)\nval sum = numbers.fold(0) { acc, i -> acc + i }\nprintln(sum) // 15\n\nval emptyList = listOf<Int>()\nval emptySum = emptyList.fold(0) { acc, i -> acc + i }\nprintln(emptySum) // 0, لا يوجد خطأ!\n\nهل ترى الفرق؟ fold تُرجع القيمة الأولية إذا كانت المجموعة فارغة. reduce في هذه الحالة ستتعطل.
الاختلافات الرئيسية بين fold و reduce في Kotlin
\n\nدعونا ننظم المعرفة. إليك جدول سيساعدك على التوجيه بسرعة:
\n\n- \n
- القيمة الأولية: في
foldتحددها بنفسك. فيreduceهي العنصر الأول من المجموعة. \n - نوع النتيجة: في
foldيمكن أن يختلف نوع النتيجة عن نوع العناصر (مثلاً، تجمع Int وتحصل على String). فيreduceيتطابق نوع النتيجة دائمًا مع نوع العناصر. \n - المجموعة الفارغة:
foldتتعامل معها بشكل طبيعي (تُرجع القيمة الأولية).reduceتُلقي استثناءً. \n - الأداء: كلتا الدالتين تعملان بزمن O(n). الفرق في الأداء غير ملحوظ. \n
الآن دعونا نرى ذلك في الكود.
\n\nمثال 1: أنواع مختلفة للمراكم والعناصر
\n\nلنفترض أن لدينا قائمة أرقام، ونريد الحصول على سلسلة نصية - مجموعها بشكل قابل للقراءة. fold تتعامل مع هذا بشكل ممتاز، لأن نوع المراكم (String) يختلف عن نوع العناصر (Int).
val numbers = listOf(1, 2, 3)\nval result = numbers.fold("المجموع: ") { acc, i -> "$acc$i " }\nprintln(result) // "المجموع: 1 2 3 "\n\nحاول فعل نفس الشيء مع reduce - سيعطيك المترجم خطأً، لأن reduce تتطلب أن يتطابق نوع المراكم مع نوع العناصر.
مثال 2: حساب المجموع مع مراعاة القيمة الأولية
\n\nمهمة شائعة: حساب المجموع الإجمالي للطلبات، لكن لدينا بالفعل رصيد أولي في الحساب.
\n\nval orders = listOf(100, 250, 75)\nval initialBalance = 500\nval totalBalance = orders.fold(initialBa