Iterator

Návrhové vzory

SSŠVT


Iterator

Obecný koncept

Iterátor je objekt, který standardizuje zpracování (procházení) datové struktury (typicky nějaké kolekce), aniž by ten, kdo iterátor používá, nutně musel znát vnitřní uspořádání takové struktury.

Pro příklad vezměme jednoduché pole textových řetězců, které inicializujeme nějakými hodnotami. Po inicializaci chceme jednotlivé prvky pole vypsat na konzoli.

Nejprve použijeme klasický postup s cyklem for a řídicí proměnnou.

// Inicializace.
string[] names = new string[4];
names[0] = "Jan";
names[1] = "Marie";
names[2] = "Lota";
names[3] = "Petr";

// Zpracování (procházení všech prvků, každý právě jednou, žádný nesmíme vynechat).
int i;
for (i = 0; i < names.Length; i++)
{
    Console.WriteLine(names[i]);
}
        

Nyní projdeme totéž pole rovněž cyklem for, avšak místo řídicí proměnné použijeme objekt se symbolickými operacemi First, Next, IsDone a CurrentItem. Tomuto objektu se říká iterátor, protože pomáhá projít (iterovat) datovou strukturu (zde pole prvků). Následující tabulka objasňuje účel a chování každé operace i to, jakou část for-cyklu zobecňuje:

Operace Popis Zobecňuje
void First() Inicializuje iterátor, nastavuje vnitřní ukazatel na první prvek v poli (prvek s indexem 0). i = 0
void Next() Provádí iteraci, tj. posune vnitřní ukazatel (inkrementuje ho) na následující prvek v poli. i++
bool IsDone() Kontroluje, zda jsme již prošli celé pole (podmínka ukončení cyklu), tj. zda vnitřní ukazatel je za posledním platným indexem daného pole. i < names.Length
string CurrentItem() Vrací aktuální prvek pole, na nějž odkazuje vnitřní ukazatel. names[i]

Nyní tedy použijeme tyto symbolické operace a symbolický objekt v kódu, který je jinak velice podobný kódu s řídicí proměnnou i.

// Inicializace.
string[] names = new string[4];
names[0] = "Jan";
names[1] = "Marie";
names[2] = "Lota";
names[3] = "Petr";

// Zpracování (procházení všech prvků, každý právě jednou, žádný nesmíme vynechat).
Iterator iter = new Iterator(names);
for (iter.First(); ! iter.IsDone(); iter.Next())
{
    Console.WriteLine(iter.CurrentItem());
}
        

Poznámka: Operace IsDone() převrací smysl původní podmínky (i < names.Length) na opačný (i >= names.Length).

Je vidět, že struktura zdrojového kódu zůstala úplně stejná, jen jsme nahradili konkrétní řídicí proměnnou cyklu a předpoklad, že známe, jak je datová struktura vnitřně uspořádána, abstraktním objektem s abstraktními operacemi, které kopírují práci s řídicí proměnnou.

Výhoda použití iterátoru je však v tom, že místo pole bychom úplně stejným způsobem prošli i jinou kolekci (např. spojový seznam), nebo dokonce složitější datovou strukturu (třeba binární strom).

O to, abychom prošli všechny prvky struktury a každý právě jednou a žádný nevynechali, se stará objekt iterátor. Klientský kód, který prvky zpracovává, to vůbec nemusí řešit. Plně se spoléhá na korektní chování iterátoru. Ten, kdo programuje klientský kód, se tedy může plně soustředit na to, čeho chce dosáhnout (v našem případě výpis prvků pole na konzoli) a neřeší podružnosti typu inicializace indexu, správná formulace podmínky ukončení cyklu, inkrement indexu v každé iteraci apod.


Rozhraní IEnumerable a IEnumerator v .NET


Klíčové slové yield