Menu
Coddy logo textTech

Iterable i Iterator

Część sekcji Programowanie obiektowe ścieżki Dart w Coddy. Lekcja 68 z 110.

Za każdym razem, gdy używasz pętli for-in na liście, zbiorze lub innej kolekcji, pod spodem korzystasz z Iterable i Iterator. Zrozumienie tych pojęć pozwala tworzyć własne klasy, po których można iterować.

Obiekt Iterable to dowolny obiekt, który może wygenerować sekwencję wartości. Ma jeden kluczowy wymóg: getter iterator, który zwraca obiekt Iterator. To Iterator faktycznie przechodzi przez elementy jeden po drugim:

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

while (iterator.moveNext()) {
  print(iterator.current);  // 1, potem 2, potem 3
}

Metoda moveNext() przechodzi do następnego elementu i zwraca true, jeśli taki istnieje, albo false, gdy iterowanie się zakończy. Właściwość current zwraca bieżący element.

Aby Twoja własna klasa była iterowalna, zaimplementuj interfejs 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
  }
}

Teraz Countdown działa z pętlami for-in i wszystkimi metodami Iterable, takimi jak map, where i fold. Ten wzorzec jest niezbędny podczas tworzenia niestandardowych kolekcji, które bezproblemowo integrują się z ekosystemem kolekcji Dart.

challenge icon

Wyzwanie

Łatwy

Stwórzmy własny generator zakresu, po którym można iterować za pomocą pętli for-in! Utworzysz własne klasy Iterable i Iterator, aby generować ciąg liczb z określonego zakresu.

Podzielisz kod na dwa pliki:

  • range.dart: Utwórz dwie współpracujące ze sobą klasy, które generują ciąg liczb:
    • Klasa RangeIterator, która implementuje Iterator<int>. Powinna śledzić bieżącą pozycję i wiedzieć, kiedy się zatrzymać. Iterator zaczyna przed pierwszym elementem, więc przed uzyskaniem dostępu do current trzeba wywołać moveNext().
    • Klasa Range, która rozszerza Iterable<int>. Przyjmuje wartości start i end w konstruktorze oraz udostępnia getter iterator, który zwraca RangeIterator.
    Zakres powinien obejmować zarówno start, jak i end. Na przykład Range(1, 5) powinien generować: 1, 2, 3, 4, 5.
  • main.dart: Zaimportuj plik z zakresem i zademonstruj własną iterowalną kolekcję:
    • Utwórz Range(1, 5) i użyj pętli for-in, aby wypisać każdą liczbę w osobnym wierszu
    • Wypisz pusty wiersz
    • Utwórz Range(10, 13), użyj metody map, aby podwoić każdą wartość, a następnie wypisz wynikową listę

Ponieważ klasa Range rozszerza Iterable, automatycznie zyskuje dostęp do wszystkich przydatnych metod kolekcji, takich jak map, where i fold — na tym polega piękno implementowania interfejsu Iterable!

Oczekiwany wynik:

1
2
3
4
5

[20, 22, 24, 26]

Spróbuj swoich sił

import 'range.dart';

void main() {
  // TODO: Utwórz Range(1, 5) i użyj pętli for-in, aby wypisać każdą liczbę
  
  
  // TODO: Wypisz pustą linię
  
  
  // TODO: Utwórz Range(10, 13), użyj map, aby podwoić każdą wartość, i wypisz listę
  
}
quiz iconSprawdź się

Ta lekcja zawiera krótki quiz. Zacznij lekcję, żeby na niego odpowiedzieć i śledzić swoje postępy.

Wszystkie lekcje w sekcji Programowanie obiektowe

Poćwicz samodzielnie: Kompilator Dart online