مرحبا يا هبر!
اسمي ألكسندر زيمين ، وأنا مطور 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 }
هذا مثال رائع لكيفية
flatMap
طريقة
flatMap
. سنقوم بتحويل (تعيين) كل عنصر من عناصر قائمة المصادر وإنشاء تسلسل جديد. بفضل
flatMap
النتيجة النهائية هي بنية مسطحة من التسلسلات المحولة.
اختياري. خريطة مسطحة <U> (_: (ملفوفة) -> U؟) -> U؟
الزائد الثاني للأنواع الاختيارية. إذا كان للنوع الاختياري الذي تتصل به قيمة ، فسيتم استدعاء الإغلاق بالقيمة بدون الغلاف الاختياري (قيمة غير ملفوفة) ، ويمكنك إرجاع القيمة الاختيارية المحولة.
let a: Int? = 2 let transformedA = a.flatMap { $0 * 2 }
Sequence.flatMap <U> (_: (Element) -> U؟) -> [U]
سيساعدك الحمل الزائد الثالث على فهم
compactMap
. تبدو هذه النسخة مماثلة للإصدار الأول ، ولكن هناك فرق مهم. في هذه الحالة ، يعود الإغلاق اختياريًا.
flatMap
بمعالجتها ، وتخطي قيم الصفر التي تم إرجاعها ، وتتضمن كل الباقي - في النتيجة كقيم بدون غلاف.
let array = [1, 2, 3, 4, nil, 5, 6, nil, 7] let arrayWithoutNils = array.flatMap { $0 }
ولكن في هذه الحالة ، لم يتم تنفيذ الطلب. لذلك ، هذا الإصدار من
flatMap
أقرب إلى
map
من التعريف الوظيفي البحت لـ
flatMap
. والمشكلة مع هذا التحميل الزائد هو أنه لا يمكنك استخدامه بشكل صحيح حيث ستعمل
map
بشكل مثالي.
let array = [1, 2, 3, 4, 5, 6] let transformed = array.flatMap { $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
.