التكرار عبر المصفوفة في Swift: الفهرس والعنصر تحت الغطاء

مدرب بايثون أونلاين للمبتدئين

تعلم Python بسهولة دون إرهاق نظري. حل مهام عملية مع التحقق التلقائي واكتب الكود مباشرة في المتصفح.

ابدأ الدورة

كيف تحصل على فهرس العنصر عند اجتياز المصفوفة في Swift؟ نفهم السؤال الشائع

\n\n

تخيل: أنت تكتب بلغة Swift وتريد ليس فقط المرور عبر المصفوفة، ولكن أيضًا معرفة موقع كل عنصر. هل تحتاج إلى تعديل العناصر حسب الفهرس، أو مقارنة القيم المجاورة، أو مجرد عرض قائمة مرقمة - المهمة واحدة. سؤال "How to iterate a loop with index and element in Swift" حصد آلاف المشاهدات على Stack Overflow. هذا يعني أن هذا الموضوع يثير اهتمام الكثيرين.

\n\n

في هذه المقالة، لن نقدم إجابة جاهزة فقط. سننظر تحت الغطاء: سنحلل الطرق الموجودة، أيها أسرع، أيها أكثر أمانًا، ولماذا أصبحت إحداها - enumerated() - المعيار الفعلي. لنبدأ!

\n\n

المشكلة: لماذا لا يكفي for-in البسيط؟

\n\n

لنفترض أن لديك مصفوفة من السلاسل النصية:

\n\n
let fruits = ["تفاح", "موز", "كرز", "شمام"]
\n\n

الطريقة الأكثر وضوحًا هي for-in:

\n\n
for fruit in fruits {\n    print(fruit)\n}
\n\n

هذا سيطبع أسماء الفواكه. ولكن ماذا لو كنت بحاجة إلى معرفة الفهرس؟ على سبيل المثال: "1. تفاح، 2. موز..."؟ for-in البسيط لا يعطي الفهرس. تضطر لاختراع حلول بديلة:

\n\n
var index = 0\nfor fruit in fruits {\n    print("\\(index). \\(fruit)")\n    index += 1\n}
\n\n

هل يعمل؟ نعم. هل هو جميل؟ لا. وماذا لو كانت المصفوفة فارغة؟ وماذا لو نسيت زيادة index عن طريق الخطأ؟ مرحبًا بك في عالم الأخطاء. تقدم Swift حلولًا أكثر أناقة.

\n\n

الطريقة رقم 1: الكلاسيكية - طريقة enumerated()

\n\n

هذه هي الطريقة الأكثر شيوعًا والموصى بها. طريقة enumerated() تُرجع تسلسلًا من الصفوف (offset: Int, element: Element). لنرى كيف يعمل هذا:

\n\n
for (index, fruit) in fruits.enumerated() {\n    print("\\(index + 1). \\(fruit)")\n}
\n\n

الإخراج:

\n
1. تفاح\n2. موز\n3. كرز\n4. شمام
\n\n

ماذا يحدث تحت الغطاء؟

\n\n

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

\n\n

ملاحظة مهمة: offset يبدأ دائمًا من 0. إذا كنت بحاجة إلى فهرس يبدأ من 1 (كما في ترقيم القوائم)، فقط أضف واحدًا. Swift لا يفعل ذلك نيابة عنك - هذا قرار متعمد من مطوري اللغة للحفاظ على التوحيد مع فهرسة المصفوفات.

\n\n

enumerated() مع forEach

\n\n

طريقة forEach تدعم أيضًا enumerated():

\n\n
fruits.enumerated().forEach { (index, fruit) in\n    print("\\(index). \\(fruit)")\n}
\n\n

الفرق بين for-in و forEach مهم: داخل forEach لا يمكن استخدام break أو continue. إذا كنت بحاجة إلى الخروج المبكر من الحلقة - استخدم for-in.

\n\n

الطريقة رقم 2: الفهرسة عبر النطاق (Range)

\n\n

طريقة قديمة ولكنها لا تزال موجودة - التكرار عبر الفهارس:

\n\n
for index in 0..<fruits.count {\n    print("\\(index). \\(fruits[index])")\n}
\n\n

يبدو الأمر بسيطًا. لكن هناك مخاطر خفية:

\n\n
    \n
  • الأمان: إذا تغيرت المصفوفة أثناء التكرار (مثلًا، في كود متعدد الخيوط)، قد يصبح fruits.count قديمًا، وستخرج عن حدود المصفوفة.
  • \n
  • قابلية القراءة: الكود يبدو أكثر "انخفاضًا في المستوى". سيقضي المطورون الآخرون ثانية إضافية لفهم ما تفعله.
  • \n
  • الأداء: الوصول عبر الفهرس fruits[index] هو عملية O(1) للمصفوفات، ولكن بالنسبة للآخرين

المدونات

توصيات الكتب