Menu
CoddyTech

Iterable và Iterator

Thuộc phần Lập trình hướng đối tượng trong hành trình Dart của Coddy. Bài học 68 trên 110.

Mỗi khi dùng vòng lặp for-in với List, Set hoặc bộ sưu tập khác, bên dưới bạn đang làm việc với Iterable và Iterator. Hiểu các khái niệm này giúp bạn tạo các lớp của riêng mình để có thể lặp qua.

Đối tượng Iterable là bất kỳ đối tượng nào có thể tạo ra một chuỗi giá trị. Đối tượng này có một yêu cầu chính: getter iterator trả về một Iterator. Iterator thực sự duyệt qua từng phần tử một:

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

while (iterator.moveNext()) {
  print(iterator.current);  // 1, rồi đến 2, rồi đến 3
}

Phương thức moveNext() chuyển đến phần tử tiếp theo và trả về true nếu còn phần tử, hoặc false khi đã hết. Thuộc tính current cho bạn biết phần tử hiện tại.

Để lớp của bạn có thể lặp qua được, hãy triển khai giao diện 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
  }
}

Giờ đây, Countdown hoạt động với vòng lặp for-in và mọi phương thức của Iterable như map, where và fold. Đây là mẫu thiết yếu khi tạo các bộ sưu tập tùy chỉnh tích hợp liền mạch với hệ sinh thái bộ sưu tập của Dart.

challenge icon

Thử thách

Dễ

Hãy tạo một bộ sinh dãy số tùy chỉnh có thể duyệt bằng vòng lặp for-in! Bạn sẽ tự tạo các lớp Iterable và Iterator để sinh một dãy số trong phạm vi đã chỉ định.

Bạn sẽ sắp xếp code thành hai tệp:

  • range.dart: Tạo hai lớp phối hợp với nhau để tạo ra một dãy số:
    • Lớp RangeIterator triển khai Iterator<int>. Lớp này cần theo dõi vị trí hiện tại và xác định khi nào cần dừng. Trình lặp bắt đầu ở vị trí trước phần tử đầu tiên, vì vậy phải gọi moveNext() trước khi truy cập current.
    • Lớp Range mở rộng Iterable<int>. Lớp này nhận giá trị start và end trong hàm khởi tạo, đồng thời cung cấp getter iterator trả về một RangeIterator.
    Phạm vi bao gồm cả start và end. Ví dụ, Range(1, 5) sẽ tạo ra: 1, 2, 3, 4, 5.
  • main.dart: Nhập tệp range của bạn và minh họa iterable tùy chỉnh:
    • Tạo Range(1, 5) và dùng vòng lặp for-in để in từng số trên một dòng riêng
    • In ra một dòng trống
    • Tạo Range(10, 13) và dùng phương thức map để nhân đôi từng giá trị, sau đó in danh sách kết quả

Vì lớp Range mở rộng Iterable, lớp này tự động có quyền sử dụng tất cả các phương thức tập hợp mạnh mẽ như map, where và fold. Đó chính là ưu điểm của việc triển khai giao diện Iterable!

Đầu ra dự kiến:

1
2
3
4
5

[20, 22, 24, 26]

Tự làm thử

import 'range.dart';

void main() {
  // TODO: Tạo Range(1, 5) và dùng vòng lặp for-in để in ra từng số
  
  
  // TODO: In ra một dòng trống
  
  
  // TODO: Tạo Range(10, 13), dùng map để nhân đôi từng giá trị rồi in ra danh sách
  
}
quiz iconTự kiểm tra

Bài học này có một bài trắc nghiệm ngắn. Bắt đầu bài học để trả lời và theo dõi tiến độ.

Tất cả bài học trong Lập trình hướng đối tượng

Tự luyện tập: Trình biên dịch Dart online