Menu
Coddy logo textTech
flag Ar iconالعربيةdown icon

Iterable و Iterator

جزء من قسم Object Oriented Programming في رحلة Dart على Coddy — الدرس 68 من 110.

في كل مرة تستخدم فيها حلقة for-in على List أو Set أو أي مجموعة أخرى، فإنك تتعامل مع Iterable و Iterator في الخلفية. فهم هذه المفاهيم يتيح لك إنشاء فئات (classes) خاصة بك يمكن تكرار المرور عليها.

يُعدّ الـ Iterable أي كائن يمكنه إنتاج تسلسل من القيم. وله متطلب أساسي واحد: وهو وجود iterator getter يُرجع Iterator. والـ Iterator هو ما ينتقل فعلياً عبر العناصر واحداً تلو الآخر:

var numbers = [1, 2, 3];
var iterator = numbers.iterator;

while (iterator.moveNext()) {
  print(iterator.current);  // 1، ثم 2، ثم 3
}

تنتقل الطريقة moveNext() إلى العنصر التالي وتُرجع true إذا كان موجوداً، أو false عند الانتهاء. وتوفر لك الخاصية current العنصر الحالي.

لجعل الكلاس الخاص بك قابلاً للتكرار، قم بتنفيذ واجهة Iterable:

class Countdown extends Iterable<int> {
  final int start;
  Countdown(this.start);
  
  @override
  Iterator<int> get iterator => CountdownIterator(start);
}

class CountdownIterator implements Iterator<int> {
  int _current;
  
  CountdownIterator(int start) : _current = start + 1;
  
  @override
  int get current => _current;
  
  @override
  bool moveNext() {
    if (_current > 0) {
      _current--;
      return true;
    }
    return false;
  }
}

void main() {
  for (var n in Countdown(3)) {
    print(n);  // 3, 2, 1, 0
  }
}

الآن يعمل Countdown مع حلقات for-in وجميع طرق Iterable مثل map و where و fold. هذا النمط ضروري عند بناء مجموعات مخصصة تتكامل بسلاسة مع نظام مجموعات Dart البيئي.

challenge icon

التحدي

سهل

لنقم ببناء مولد نطاق (range generator) مخصص يمكن تكراره باستخدام حلقة for-in! ستقوم بإنشاء فئات Iterable و Iterator الخاصة بك لإنشاء تسلسل من الأرقام ضمن نطاق محدد.

ستقوم بتنظيم الكود الخاص بك في ملفين:

  • range.dart: قم بإنشاء فئتين تعملان معاً لإنتاج تسلسل من الأرقام:
    • فئة RangeIterator التي تنفذ Iterator<int>. يجب أن تتبع الموضع الحالي وتعرف متى تتوقف. يبدأ المكرر (iterator) قبل العنصر الأول، لذا يجب استدعاء moveNext() قبل الوصول إلى current.
    • فئة Range التي ترث من Iterable<int>. تأخذ قيمتي start و end في منشئها (constructor) وتوفر getter لـ iterator يعيد كائن RangeIterator.
    يجب أن يشمل النطاق كلاً من start و end. على سبيل المثال، Range(1, 5) يجب أن ينتج: 1، 2، 3، 4، 5.
  • main.dart: قم باستيراد ملف النطاق الخاص بك واستعرض الـ iterable المخصص:
    • أنشئ Range(1, 5) واستخدم حلقة for-in لطباعة كل رقم في سطر منفصل
    • اطبع سطراً فارغاً ''
    • أنشئ Range(10, 13) واستخدم طريقة map لمضاعفة كل قيمة، ثم اطبع القائمة الناتجة

بما أن فئة Range الخاصة بك ترث من Iterable، فإنها تحصل تلقائياً على إمكانية الوصول إلى جميع طرق المجموعات القوية مثل map و where و fold - وهذا هو جمال تنفيذ واجهة Iterable!

المخرجات المتوقعة:

1
2
3
4
5

[20, 22, 24, 26]

ورقة مرجعية

الـ Iterable هو أي كائن يمكنه إنتاج تسلسل من القيم. يتطلب getter باسم iterator يعيد Iterator.

يتحرك الـ Iterator عبر العناصر واحداً تلو الآخر باستخدام:

  • moveNext() - ينتقل إلى العنصر التالي ويعيد true إذا كان هناك عنصر، أو false عند الانتهاء
  • current - خاصية تمنحك العنصر الحالي

استخدام iterator يدوياً:

var numbers = [1, 2, 3];
var iterator = numbers.iterator;

while (iterator.moveNext()) {
  print(iterator.current);  // 1، ثم 2، ثم 3
}

لجعل فئة (class) مخصصة قابلة للتكرار (iterable)، قم بتنفيذ واجهة Iterable وإنشاء فئة Iterator مقابلة:

class Countdown extends Iterable<int> {
  final int start;
  Countdown(this.start);
  
  @override
  Iterator<int> get iterator => CountdownIterator(start);
}

class CountdownIterator implements Iterator<int> {
  int _current;
  
  CountdownIterator(int start) : _current = start + 1;
  
  @override
  int get current => _current;
  
  @override
  bool moveNext() {
    if (_current > 0) {
      _current--;
      return true;
    }
    return false;
  }
}

void main() {
  for (var n in Countdown(3)) {
    print(n);  // 3، 2، 1، 0
  }
}

الفئات التي ترث من Iterable تعمل مع حلقات for-in وتحصل تلقائياً على إمكانية الوصول إلى جميع دوال Iterable مثل map و where و fold.

جرّب بنفسك

import 'range.dart';

void main() {
  // TODO: قم بإنشاء Range(1, 5) واستخدم حلقة for-in لطباعة كل رقم
  
  
  // TODO: اطبع سطرًا فارغًا
  
  
  // TODO: قم بإنشاء Range(10, 13)، واستخدم map لمضاعفة كل قيمة، واطبع القائمة
  
}
quiz iconاختبر نفسك

يتضمن هذا الدرس اختبارًا قصيرًا. ابدأ الدرس للإجابة عليه وتتبّع تقدمك.

جميع دروس Object Oriented Programming