Swift 4.1: لماذا أعادت Apple تسمية FlatMap إلى CompactMap

مرحبا يا هبر!

اسمي ألكسندر زيمين ، وأنا مطور iOS في Badoo. هذه ترجمة لمقالة كتبها زميلي شويب ، حيث وصف كيف كانت وظيفة FlatMap في Swift ولماذا تمت إعادة تسمية إحدى الأحمال الزائدة الخاصة بها على CompactMap. المقالة مفيدة لفهم العمليات التي تحدث في مستودع Swift وتطورها ، وكذلك للتطوير العام.



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



للعديد من الإصدارات ، يحتوي Swift على map flatMap . ومع ذلك ، في Swift 4.1 ، لم يعد بإمكانك تطبيق flatMap على تسلسل من القيم ولا يزال flatMap تمرير إغلاق يعيد قيمة اختيارية. يوجد الآن طريقة compactMap .

في البداية ، قد لا يكون من السهل فهم جوهر الابتكار. إذا عملت flatMap بشكل جيد ، فلماذا تقدم طريقة منفصلة؟ دعونا نكتشف ذلك.

قدمت مكتبة Swift القياسية قبل الإصدار 4.1 ثلاثة تطبيقات flatMap الزائد flatMap :

 1. Sequence.flatMap<S>(_: (Element) -> S) -> [S.Element],  S : Sequence 2. Optional.flatMap<U>(_: (Wrapped) -> U?) -> U? 3. Sequence.flatMap<U>(_: (Element) -> U?) -> [U] 

دعونا نذهب من خلال الخيارات الثلاثة ونرى ما يفعلونه.

Sequence.flatMap <S> (_: (Element) -> S) -> [S.Element] ، حيث S: Sequence


الحمل الزائد الأول للتسلسلات التي يأخذ فيها الإغلاق عنصرًا من هذا التسلسل ويتحول إلى تسلسل آخر.
flatMap كل هذه التتابعات المحولة إلى التسلسل النهائي الذي تم إرجاعه كنتيجة. على سبيل المثال:

 let array = [[1, 2, 3], [4, 5, 6], [7, 8, 9]] let flattened = array.flatMap { $0 } // [1, 2, 3, 4, 5, 6, 7, 8, 9] 

هذا مثال رائع لكيفية flatMap طريقة flatMap . سنقوم بتحويل (تعيين) كل عنصر من عناصر قائمة المصادر وإنشاء تسلسل جديد. بفضل flatMap النتيجة النهائية هي بنية مسطحة من التسلسلات المحولة.

اختياري. خريطة مسطحة <U> (_: (ملفوفة) -> U؟) -> U؟


الزائد الثاني للأنواع الاختيارية. إذا كان للنوع الاختياري الذي تتصل به قيمة ، فسيتم استدعاء الإغلاق بالقيمة بدون الغلاف الاختياري (قيمة غير ملفوفة) ، ويمكنك إرجاع القيمة الاختيارية المحولة.

 let a: Int? = 2 let transformedA = a.flatMap { $0 * 2 } // 4 let b: Int? = nil let transformedB = b.flatMap { $0 * 2 } // nil 

Sequence.flatMap <U> (_: (Element) -> U؟) -> [U]


سيساعدك الحمل الزائد الثالث على فهم compactMap . تبدو هذه النسخة مماثلة للإصدار الأول ، ولكن هناك فرق مهم. في هذه الحالة ، يعود الإغلاق اختياريًا. flatMap بمعالجتها ، وتخطي قيم الصفر التي تم إرجاعها ، وتتضمن كل الباقي - في النتيجة كقيم بدون غلاف.

 let array = [1, 2, 3, 4, nil, 5, 6, nil, 7] let arrayWithoutNils = array.flatMap { $0 } // [1, 2, 3, 4, 5, 6, 7] 

ولكن في هذه الحالة ، لم يتم تنفيذ الطلب. لذلك ، هذا الإصدار من flatMap أقرب إلى map من التعريف الوظيفي البحت لـ flatMap . والمشكلة مع هذا التحميل الزائد هو أنه لا يمكنك استخدامه بشكل صحيح حيث ستعمل map بشكل مثالي.

 let array = [1, 2, 3, 4, 5, 6] let transformed = array.flatMap { $0 } // same as array.map { $0 } 

يتوافق استخدام flatMap مع الحمل الزائد الثالث ، حيث يلف ضمنيًا القيمة المحولة في اختياري ، ثم يزيل الغلاف لإضافته إلى النتيجة. يصبح الوضع مثيرًا للاهتمام بشكل خاص إذا لم يتم استخدام تحويلات السلسلة بشكل صحيح.

 struct Person { let name: String } let people = [Person(name: “Foo”), Person(name: “Bar”)] let names = array.flatMap { $0.name } 

في Swift قبل الإصدار 4.0 ، سنحصل على تحويل إلى [“Foo”, “Bar”] . ولكن بدءًا من الإصدار 4.0 ، تطبق قيم السلسلة بروتوكول المجموعة. لذلك ، فإن استخدامنا لـ flatMap في هذه الحالة ، بدلاً من الحمل الزائد الثالث ، سوف يتوافق مع الأول ، وسوف نحصل على نتيجة "مسطحة" من القيم المحولة: [“F”, “o”, “o”, “B”, “a”, “r”]

عند استدعاء flatMap لن تحصل على خطأ ، لأن هذا مسموح بالاستخدام. لكن المنطق سيُكسر ، لأن النتيجة من النوع Array<Character>.Type ، وليس Array<String>.Type المتوقع Array<String>.Type .

الخلاصة


لتجنب سوء استخدام flatMap ، تمت إزالة الإصدار الثالث الزائد من إصدار Swift الجديد. ولحل نفس المشكلة (إزالة القيم compactMap ) الآن تحتاج إلى استخدام طريقة منفصلة - compactMap .

Source: https://habr.com/ru/post/ar414809/


All Articles