Iterátor - Iterator

V programování počítače , An iterator je objekt , který umožňuje programátorovi přejít na kontejner , zejména seznamy . Přes rozhraní kontejneru se často poskytují různé typy iterátorů . Ačkoli rozhraní a sémantika daného iterátoru jsou pevné, iterátory jsou často implementovány z hlediska struktur, které jsou základem implementace kontejneru, a jsou často pevně spojeny s kontejnerem, aby umožnily provozní sémantiku iterátoru. Iterátor provádí traversal a také poskytuje přístup k datovým prvkům v kontejneru, ale sám neprovádí iteraci (tj. Bez určité významné svobody přijaté s tímto konceptem nebo s triviálním použitím terminologie). Iterátor je chování podobný kurzoru databáze . Iterátoři se datují do programovacího jazyka CLU v roce 1974.

Popis

Interní iterátoři

Interní iterátory jsou funkce vyššího řádu (často využívající anonymní funkce ), jako je mapování , zmenšování atd., Implementace prochodu napříč kontejnerem, aplikování dané funkce na každý prvek.

Externí iterátory a vzor iterátoru

Externí iterátor lze považovat za typ ukazatele, který má dvě primární operace: odkazování na jeden konkrétní prvek v kolekci objektů (nazývaný přístup k prvku ) a úprava sama tak, aby ukazovala na další prvek (nazývaný procházení prvku ). Musí také existovat způsob, jak vytvořit iterátor, takže ukazuje na nějaký první prvek, stejně jako nějaký způsob, jak určit, kdy iterátor vyčerpal všechny prvky v kontejneru. V závislosti na jazyce a zamýšleném použití mohou iterátory také poskytovat další operace nebo vykazovat odlišné chování.

Primárním účelem iterátoru je umožnit uživateli zpracovat každý prvek kontejneru při izolaci uživatele z vnitřní struktury kontejneru. To umožňuje kontejneru ukládat prvky jakýmkoli způsobem, který si přeje, a zároveň umožňuje uživateli s ním zacházet, jako by to byla jednoduchá sekvence nebo seznam. Třída iterátoru je obvykle navržena v těsné koordinaci s odpovídající třídou kontejneru. Kontejner obvykle poskytuje metody pro vytváření iterátorů.

Smyčkové počítadlo je někdy také označována jako smyčky iterátor. Smyčkové počítadlo , však poskytuje pouze funkce traversal a není funkčnost přístupový prvek.

Generátory

Jedním ze způsobů implementace iterátorů je použití omezené formy korutinu , známé jako generátor . Na rozdíl od podprogramu může generátorový rutina přinést hodnoty volajícímu několikrát, místo aby se vrátil jen jednou. Většina iterátorů je přirozeně vyjádřitelná jako generátory, ale protože generátory zachovávají svůj místní stav mezi vyvoláními, hodí se zvláště pro komplikované, stavové iterátory, jako jsou procházení stromů . V používání termínů „generátor“ a „iterátor“ existují jemné rozdíly a rozdíly, které se mezi autory a jazyky liší. V Pythonu je generátor konstruktor iterátoru : funkce, která vrací iterátor. Následuje příklad generátoru Pythonu, který vrací iterátor pro čísla Fibonacci pomocí příkazu Pythonu yield:

def fibonacci(limit):
    a, b = 0, 1
    for _ in range(limit):
        yield a
        a, b = b, a + b

for number in fibonacci(100): # The generator constructs an iterator
    print(number)

Implicitní iterátory

Některé objektově orientované jazyky, jako jsou C # , C ++ (novější verze), Delphi (novější verze), Go , Java (novější verze), Lua , Perl , Python , Ruby, nabízejí vlastní způsob iterace prvky kontejnerového objektu bez zavedení explicitního objektu iterátoru. Skutečný iterátorový objekt může ve skutečnosti existovat, ale pokud ano, není vystaven ve zdrojovém kódu jazyka.

Implicitní iterátory se často projevují příkazem " foreach " (nebo ekvivalentem), například v následujícím příkladu Pythonu:

for value in iterable:
    print(value)

V Pythonu je iterovatelný objekt, který lze převést na iterátor, který je poté iterován během cyklu for; to se děje implicitně.

Nebo jindy mohou být vytvořeny samotným objektem kolekce, jako v tomto příkladu Ruby:

iterable.each do |value|
  puts value
end

Tento styl iterace se někdy nazývá „interní iterace“, protože jeho kód se plně spouští v kontextu iterovatelného objektu (který řídí všechny aspekty iterace) a programátor poskytuje pouze operaci, která se má provést v každém kroku (pomocí anonymní funkce ).

Jazyky, které podporují porozumění seznamu nebo podobné konstrukce, mohou také při konstrukci seznamu výsledků využívat implicitní iterátory, jako v Pythonu:

names = [person.name for person in roster if person.male]

Někdy je implicitní skrytá povaha jen částečná. Jazyk C ++ má několik šablon funkcí pro implicitní iteraci, například for_each(). Tyto funkce stále vyžadují explicitní iterátorové objekty jako svůj počáteční vstup, ale následná iterace nevystavuje uživateli iterátorový objekt.

Proudy

Iterátory jsou užitečnou abstrakcí vstupních proudů - poskytují potenciálně nekonečný iterovatelný (ale ne nutně indexovatelný) objekt. Několik jazyků, například Perl a Python, implementují proudy jako iterátory. V Pythonu jsou iterátory objekty představující proudy dat. Alternativní implementace proudu zahrnují jazyky založené na datech , jako jsou AWK a sed .

V kontrastu s indexací

V procedurálních jazycích je běžné používat operátor dolního indexu a čítač smyčky k procházení všemi prvky v sekvenci, jako je například pole. I když lze indexování použít také u některých objektově orientovaných kontejnerů, použití iterátorů může mít některé výhody:

  • Počítací smyčky nejsou vhodné pro všechny datové struktury, zejména pro datové struktury bez nebo s pomalým náhodným přístupem , jako jsou seznamy nebo stromy .
  • Iterátory mohou poskytnout konzistentní způsob iterace na datových strukturách všeho druhu, a proto učinit kód čitelnějším, opakovaně použitelným a méně citlivým na změnu datové struktury.
  • Iterátor může vynutit další omezení přístupu, například zajistit, že prvky nelze přeskočit nebo že k dříve navštívenému prvku nebude možné přistupovat podruhé.
  • Iterátor může umožnit úpravu objektu kontejneru bez zneplatnění iterátoru. Například jakmile iterátor postoupil za první prvek, je možné vložit další prvky na začátek kontejneru s předvídatelnými výsledky. S indexováním je to problematické, protože čísla indexů se musí změnit.

Schopnost kontejneru být modifikován při iteraci jeho prvky se stala nezbytnou v moderním objektově orientovaném programování, kde nemusí být zřejmé vzájemné vztahy mezi objekty a účinky operací. Použitím iterátoru je člověk izolován od těchto druhů následků. Toto tvrzení je však třeba brát s rezervou, protože častěji než z důvodu efektivity je implementace iterátoru tak pevně svázána s kontejnerem, že vylučuje modifikaci podkladového kontejneru bez zneplatnění.

U kontejnerů, které se mohou pohybovat kolem svých dat v paměti, je jediný způsob, jak zneplatnit iterátor, pro kontejner nějak sledovat všechny aktuálně živé iterátory a průběžně je aktualizovat. Vzhledem k tomu, že počet iterátorů v daném čase může být libovolně velký ve srovnání s velikostí svázaného kontejneru, jejich aktualizace všechny drasticky zhorší záruku složitosti operací kontejneru.

Alternativním způsobem, jak udržet počet aktualizací vázaných relativně k velikosti kontejneru, by bylo použití druhu mechanismu úchytu, což je kolekce nepřímých ukazatelů na prvky kontejneru, které musí být aktualizovány s kontejnerem, a nechat iterátory odkazovat na tyto úchyty místo přímo k datovým prvkům. Ale tento přístup bude mít negativní dopad na výkon iterátoru, protože musí následovat dvojitý ukazatel následující pro přístup ke skutečnému datovému prvku. To obvykle není žádoucí, protože mnoho algoritmů využívajících iterátory vyvolává operaci přístupu k datům iterátorů častěji než metoda předem. Proto je obzvláště důležité mít iterátory s velmi efektivním přístupem k datům.

Celkově vzato jde vždy o kompromis mezi zabezpečením (iterátory zůstávají vždy platné) a efektivitou. Většinu času přidaná bezpečnost nestojí za cenu efektivity, kterou byste za ni zaplatili. Použití alternativního kontejneru (například jednotlivě propojeného seznamu namísto vektoru) by bylo lepší volbou (globálně efektivnější), pokud je potřeba stabilita iterátorů.

Klasifikace iterátorů

Kategorie iterátorů

Iterátory lze rozdělit do kategorií podle jejich funkčnosti. Zde je (neúplný) seznam kategorií iterátorů:

Kategorie Jazyky
Obousměrný iterátor C ++
Dopředný iterátor C ++
Vstupní iterátor C ++
Výstupní iterátor C ++
Iterátor s náhodným přístupem C ++
Triviální iterátor C ++ (starý STL )

Typy iterátorů

Různé jazyky nebo knihovny používané s těmito jazyky definují typy iterátorů. Někteří z nich jsou

Typ Jazyky
Pole iterátoru PHP , R.
Ukládání do mezipaměti iterátor PHP
Konstantní iterátor C ++ , PHP
Iterátor adresáře PHP, Python
Iterátor filtru PHP, R.
Omezit iterátor PHP
Seznam iterátorů Java , R.
Rekurzivní iterátor pole PHP
Iterátor XML PHP

V různých programovacích jazycích

C # a další jazyky .NET

Iterátory v .NET Framework se nazývají „enumerátory“ a představují IEnumeratorrozhraní. IEnumeratorposkytuje MoveNext()metodu, která postupuje k dalšímu prvku a označuje, zda bylo dosaženo konce kolekce; Currentvlastnost, získat hodnotu prvku právě ukázal na; a volitelná Reset()metoda k převinutí enumerátoru zpět do původní polohy. Výčet nejprve ukazuje na speciální hodnotu před prvním prvkem, takže MoveNext()k zahájení iterace je nutné volání na .

Enumerátory se obvykle získávají voláním GetEnumerator()metody objektu implementujícího IEnumerablerozhraní. Třídy kontejnerů obvykle implementují toto rozhraní. Nicméně, foreach prohlášení v jazyce C # mohou pracovat na jakýkoli předmět poskytující takovou metodu, i když to neimplementuje IEnumerable( kachní psaní ). Obě rozhraní byla rozšířena do obecných verzí v .NET 2.0 .

Následující příklad ukazuje jednoduché použití iterátorů v C # 2.0:

// explicit version
IEnumerator<MyType> iter = list.GetEnumerator();
while (iter.MoveNext())
    Console.WriteLine(iter.Current);

// implicit version
foreach (MyType value in list)
    Console.WriteLine(value);

C # 2.0 také podporuje generátory : metoda, která je deklarována jako vracející se IEnumerator(nebo IEnumerable), ale používá příkaz " yield return" k vytvoření sekvence prvků namísto vrácení instance objektu, bude kompilátorem transformována do nové třídy implementující příslušné rozhraní .

C ++

Jazyk C ++ ve své standardní knihovně široce využívá iterátory a popisuje několik kategorií iterátorů, které se liší v repertoáru operací, které umožňují. Patří mezi ně dopředné iterátory , obousměrné iterátory a iterátory s náhodným přístupem , aby se zvýšily možnosti. Všechny standardní typy šablon kontejnerů poskytují iterátory jedné z těchto kategorií. Iterátory zobecňují ukazatele na prvky pole (které lze skutečně použít jako iterátory) a jejich syntaxe je navržena tak, aby připomínala aritmetiku ukazatele C , kde se operátory a používají k odkazování na prvek, na který iterátor ukazuje a aritmetické operátory ukazatele jako se používají k úpravě iterátorů při přechodu kontejneru. *->++

Procházení pomocí iterátorů obvykle zahrnuje jeden variabilní iterátor a dva pevné iterátory, které slouží k ohraničení rozsahu, který má být překonán. Vzdálenost mezi omezujícími iterátory, pokud jde o počet aplikací operátoru ++potřebných k transformaci dolní hranice na horní, se rovná počtu položek v určeném rozsahu; počet zřetelných hodnot iterátoru je o jednu víc. Podle konvence dolní limitující iterátor „ukazuje na“ první prvek v rozsahu, zatímco horní omezující iterátor neukazuje na žádný prvek v rozsahu, ale těsně za koncem rozsahu. Pro procházení celého kontejneru begin()poskytuje metoda dolní limit a end()horní limit. Ten vůbec neodkazuje na žádný prvek kontejneru, ale je platnou iterační hodnotou, kterou lze porovnat.

Následující příklad ukazuje typické použití iterátoru.

std::vector<int> items;
items.push_back(5); // Append integer value '5' to vector 'items'.
items.push_back(2); // Append integer value '2' to vector 'items'.
items.push_back(9); // Append integer value '9' to vector 'items'.

for (auto it = items.begin(); it != items.end(); ++it) { // Iterate through 'items'.
  std::cout << *it; // And print value of 'items' for current index.
}
// In C++11, the same can be done without using any iterators:
for (auto x : items) {
  std::cout << x; // Print value of each element 'x' of 'items'.
}

// Each loops print "529".

Typy iterátorů jsou oddělené od typů kontejnerů, s nimiž se používají, i když se tyto dva typy často používají ve shodě. Kategorie iterátoru (a tedy operace pro něj definované) obvykle závisí na typu kontejneru, například pole nebo vektory poskytující iterátory s náhodným přístupem, ale sady (které jako implementaci používají propojenou strukturu) poskytují pouze obousměrné iterátory. Jeden stejný typ kontejneru může mít více než jeden přidružený typ iterátoru; například std::vector<T>typ kontejneru umožňuje procházení buď (surovými) ukazateli na jeho prvky (typu *<T>), nebo hodnotami speciálního typu std::vector<T>::iterator, a pro „reverzní iterátory“, jejichž operace jsou definovány takovým způsobem, že Algoritmus provádějící obvyklý (dopředný) traversal ve skutečnosti provede traversal v opačném pořadí při volání s reverzními iterátory. Většina kontejnerů také poskytuje samostatný const_iteratortyp, pro který nejsou záměrně definovány operace, které by umožnily změnu hodnot, na které ukazuje.

Jednoduché procházení kontejnerového objektu nebo rozsahu jeho prvků (včetně modifikace těchto prvků, pokud const_iteratornení použito a) lze provést pouze pomocí iterátorů. Ale typy kontejnerů mohou také poskytovat metody jako insertnebo ty, erasekteré upravují strukturu samotného kontejneru; toto jsou metody třídy kontejneru, ale navíc k určení požadované operace vyžadují jednu nebo více hodnot iterátoru. I když je možné mít více iterátorů směřujících do stejného kontejneru současně, operace úpravy struktury mohou zneplatnit určité hodnoty iterátoru (standard specifikuje pro každý případ, zda to tak může být); použití zneplatněného iterátoru je chyba, která povede k nedefinovanému chování, a tyto chyby nemusí být systémem běhu signalizovány.

Implicitní iterace je také částečně podporována C ++ pomocí standardních šablon funkcí, jako například std::for_each(), std::copy() a std::accumulate().

Při použití musí být inicializovány existujícími iterátory, obvykle begina end, které definují rozsah, ve kterém dochází k iteraci. Žádný explicitní objekt iterátoru se ale následně neobjeví, jak iterace pokračuje. Tento příklad ukazuje použití for_each.

ContainerType<ItemType> c; // Any standard container type of ItemType elements.

void ProcessItem(const ItemType& i) { // Function that will process each item of the collection.
  std::cout << i << std::endl;
}

std::for_each(c.begin(), c.end(), ProcessItem); // A for-each iteration loop.

Totéž lze dosáhnout pomocí std::copypředávání std::ostream_iteratorhodnoty jako třetího iterátoru:

std::copy(c.begin(), c.end(), std::ostream_iterator<ItemType>(std::cout, "\n"));

Vzhledem k tomu, C ++ 11 , lambda funkci syntax může být použit k určení pro operace bude opakována inline, čímž odpadá nutnost definovat pojmenovanou funkci. Zde je příklad pro každou iteraci pomocí funkce lambda:

ContainerType<ItemType> c; // Any standard container type of ItemType elements.

// A for-each iteration loop with a lambda function.
std::for_each(c.begin(), c.end(), [](const ItemType& i) { std::cout << i << std::endl; });

Jáva

Představené ve verzi Java JDK 1.2, java.util.Iteratorrozhraní umožňuje iteraci tříd kontejnerů. Každá IteratorPoskytuje next()a hasNext()způsob, a může případně podporovat remove()metodu. Iterátory jsou vytvářeny odpovídající třídou kontejneru, obvykle metodou s názvem iterator().

next()Způsob zálohy iterátor a vrátí hodnotu, na který ukazuje iterátor. První prvek se získá při prvním volání next(). K určení, kdy byly navštíveny všechny prvky v kontejneru, se používá hasNext()testovací metoda. Následující příklad ukazuje jednoduché použití iterátorů:

Iterator iter = list.iterator();
// Iterator<MyType> iter = list.iterator(); // in J2SE 5.0
while (iter.hasNext()) {
    System.out.print(iter.next());
    if (iter.hasNext())
        System.out.print(", ");
}

Abychom ukázali, že hasNext()lze opakovaně volat, používáme to k vložení čárky mezi prvky, ale ne za poslední prvek.

Tento přístup správně neodděluje operaci zálohy od skutečného přístupu k datům. Pokud musí být datový prvek pro každý postup použit více než jednou, musí být uložen v dočasné proměnné. Pokud je nutná záloha bez přístupu k datům (tj. K přeskočení daného datového prvku), přístup se přesto provede, ačkoli vrácená hodnota je v tomto případě ignorována.

U typů kolekcí, které ji podporují, remove()metoda iterátoru odstraní naposledy navštívený prvek z kontejneru při zachování použitelnosti iterátoru. Přidáním nebo odebráním prvků voláním metod kontejneru (také ze stejného vlákna ) je iterátor nepoužitelný. Pokus o získání dalšího prvku vyvolá výjimku. Výjimka je také vyvolána, pokud už nezůstávají žádné další prvky ( hasNext()dříve vrátila hodnotu false).

Navíc java.util.Listexistuje pro java.util.ListIterators podobným API, ale to umožňuje dopředu a dozadu iteraci, poskytuje jeho aktuální index v seznamu a umožňuje nastavení prvku seznamu na jeho pozici.

Vydání J2SE 5.0 Java představilo Iterablerozhraní pro podporu vylepšené for( foreach ) smyčky pro iteraci přes kolekce a pole. Iterabledefinuje iterator()metodu, která vrací Iterator. Pomocí vylepšené forsmyčky lze předchozí příklad přepsat na

for (MyType obj : list) {
    System.out.print(obj);
}

Některé kontejnery také používají starší Enumerationtřídu (od verze 1.0) . Poskytuje hasMoreElements()a nextElement()metody, ale nemá žádné metody pro úpravu kontejneru.

Scala

V Scale mají iterátory bohatou sadu metod podobných kolekcím a lze je použít přímo ve smyčkách. Ve skutečnosti, jak iterátory, tak sbírky zdědily od společného základního znaku - scala.collection.TraversableOnce. Nicméně, vzhledem k bohatou sadu metod dostupných v knihovních Scala, například map, collect, filteratd., Je to často není nutné zabývat se iterátorů přímo při programování v Scala.

Iterátory Java a sbírky lze automaticky převést na iterátory a sbírky Scala jednoduše přidáním jediného řádku

import scala.collection.JavaConversions._

do souboru. JavaConversionsObjekt poskytuje implicitní převody, jak toho dosáhnout. Implicitní převody jsou funkcí Scala: metody, které, když jsou viditelné v aktuálním oboru, automaticky vloží volání sebe do příslušných výrazů na příslušném místě, aby byly typyeck, když by jinak nebyly.

MATLAB

MATLAB podporuje externí i interní implicitní iteraci pomocí „nativních“ polí nebo cellpolí. V případě externí iterace, kde je břemeno na uživateli, aby posunul traverz a vyžádal si další prvky, lze definovat sadu prvků v rámci struktury úložiště pole a procházet prvky pomocí forkonstrukce -loop. Například,

% Define an array of integers
myArray = [1,3,5,7,11,13];

for n = myArray
   % ... do something with n
   disp(n)  % Echo integer to Command Window
end

prochází pole celých čísel pomocí forklíčového slova.

V případě interní iterace, kdy může uživatel zadat operaci iterátoru, aby provedla každý prvek kolekce, je mnoho přenašečů a funkcí MATLAB přetíženo, aby se provedlo přes každý prvek pole a implicitně vrátilo odpovídající výstupní pole . Kromě toho lze funkce arrayfuna cellfunvyužít k provádění vlastních nebo uživatelem definovaných operací nad „nativními“ poli a cellpříslušnými poli. Například,

function simpleFun
% Define an array of integers
myArray = [1,3,5,7,11,13];

% Perform a custom operation over each element 
myNewArray = arrayfun(@(a)myCustomFun(a),myArray);

% Echo resulting array to Command Window
myNewArray

function outScalar = myCustomFun(inScalar)
% Simply multiply by 2
outScalar = 2*inScalar;

definuje primární funkci, simpleFunkterá implicitně aplikuje vlastní dílčí funkci myCustomFunna každý prvek pole pomocí vestavěné funkce arrayfun.

Alternativně může být žádoucí abstrahovat mechanismy kontejneru úložiště pole od uživatele definováním vlastní objektově orientované implementace MATLAB vzoru Iterator. Taková implementace podporující externí iteraci je demonstrována v MATLAB Central File Exchange item Design Pattern: Iterator (Behavioral) . Toto je napsáno v nové syntaxi definice třídy zavedené v softwaru MATLAB verze 7.6 (R2008a) a obsahuje cellrealizaci jednorozměrného pole seznamu abstraktních datových typů (ADT) jako mechanismu pro ukládání heterogenní (v datovém typu) sady Prvky. Poskytuje funkce pro explicitní předávání seznamu vpřed s hasNext(), next()a reset()metody pro použití v while-loop.

PHP

PHP je foreachsmyčka byla zavedena ve verzi 4.0 a také v souladu s předměty, jako jsou hodnoty v 4.0 Beta 4. Nicméně, byla přidána podpora iterátorů v PHP 5 zavedením vnitřní Traversablerozhraní. Dvě hlavní rozhraní pro implementaci ve skriptech PHP, která umožňují iteraci objektů přes foreachsmyčku, jsou Iteratora IteratorAggregate. Ten nevyžaduje, aby implementující třída deklarovala všechny požadované metody, místo toho implementuje metodu accessor ( getIterator), která vrací instanci Traversable. Standardní PHP knihovna poskytuje několik tříd na práci se speciálními iterátory. PHP také podporuje Generátory od 5.5.

Nejjednodušší implementací je zabalení pole, což může být užitečné pro tipování typu a skrytí informací .

namespace Wikipedia\Iterator;

final class ArrayIterator extends \Iterator
{
    private array $array;

    public function __construct(array $array)
    {
        $this->array = $array;
    }

    public function rewind(): void
    {
        echo 'rewinding' , PHP_EOL;
        reset($this->array);
    }

    public function current()
    {
        $value = current($this->array);
        echo "current: {$value}", PHP_EOL;
        return $value;
    }

    public function key()
    {
        $key = key($this->array);
        echo "key: {$key}", PHP_EOL;
        return $key;
    }

    public function next()
    {
        $value = next($this->array);
        echo "next: {$value}", PHP_EOL;
        return $value;
    }

    public function valid(): bool
    {
        $valid = $this->current() !== false;
        echo 'valid: ', ($valid ? 'true' : 'false'), PHP_EOL;
        return $valid;
    }
}

Všechny metody příkladové třídy se používají během provádění úplné smyčky foreach ( foreach ($iterator as $key => $current) {}). Metody iterátoru jsou prováděny v následujícím pořadí:

  1. $iterator->rewind() zajišťuje, že vnitřní struktura začíná od začátku.
  2. $iterator->valid()v tomto příkladu vrátí true .
  3. $iterator->current()vrácená hodnota je uložena v $value.
  4. $iterator->key()vrácená hodnota je uložena v $key.
  5. $iterator->next() postupuje k dalšímu prvku ve vnitřní struktuře.
  6. $iterator->valid()vrací false a smyčka je přerušena.

Následující příklad ilustruje třídu PHP, která implementuje Traversablerozhraní, které by bylo možné zabalit do IteratorIteratortřídy, která bude reagovat na data před jejich vrácením do foreachsmyčky. Použití společně s MYSQLI_USE_RESULTkonstantou umožňuje skriptům PHP iterovat výsledné sady s miliardami řádků při velmi malém využití paměti. Tyto funkce nejsou exkluzivní pro PHP ani pro jeho implementace tříd MySQL (např. PDOStatementTřída implementuje také Traversablerozhraní).

mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
$mysqli = new \mysqli('host.example.com', 'username', 'password', 'database_name');

// The \mysqli_result class that is returned by the method call implements the internal Traversable interface.
foreach ($mysqli->query('SELECT `a`, `b`, `c` FROM `table`', MYSQLI_USE_RESULT) as $row) {
    // Act on the returned row, which is an associative array.
}

Krajta

Iterátory v Pythonu jsou základní součástí jazyka a v mnoha případech jsou neviditelné, protože se implicitně používají v příkazu for( foreach ), v seznamech s porozuměním a ve výrazech generátoru . Všechny standardní vestavěné typy kolekcí Pythonu podporují iteraci a také mnoho tříd, které jsou součástí standardní knihovny. Následující příklad ukazuje typickou implicitní iteraci přes sekvenci:

for value in sequence:
    print(value)

Slovníky Pythonu (forma asociativního pole ) lze také přímo iterovat, když jsou vráceny klíče slovníku; nebo items()metodu slovníku lze iterovat, kde získá odpovídající dvojici klíčů a hodnot jako n-tici:

for key in dictionary:
    value = dictionary[key]
    print(key, value)
for key, value in dictionary.items():
    print(key, value)

Iterátory však lze použít a explicitně definovat. U libovolného iterovatelného typu sekvence nebo třídy se integrovaná funkce iter()používá k vytvoření iterátorového objektu. Objekt iterátoru lze poté iterovat pomocí next()funkce, která __next__()interně používá metodu, která vrací další prvek v kontejneru. (Předchozí příkaz platí pro Python 3.x. V Pythonu 2.x je next()metoda ekvivalentní.) StopIterationVýjimka bude vyvolána, když už nezůstanou žádné další prvky. Následující příklad ukazuje ekvivalentní iteraci přes sekvenci pomocí explicitních iterátorů:

it = iter(sequence)
while True:
    try:
        value = it.next() # in Python 2.x
        value = next(it) # in Python 3.x
    except StopIteration:
        break
    print(value)

Libovolná třída definovaná uživatelem může podporovat standardní iteraci (implicitní nebo explicitní) definováním __iter__()metody, která vrací objekt iterátoru. Objekt iterátoru poté potřebuje definovat __next__()metodu, která vrací další prvek.

Generátory Pythonu implementují tento iterační protokol .

Raku

Iterátory v Raku jsou základní součástí jazyka, ačkoli uživatelé se obvykle o iterátory nemusí starat. Jejich použití je skryt za iterace API, jako je například forprohlášení map, grepseznam indexování s .[$idx], atd.

Následující příklad ukazuje typickou implicitní iteraci přes kolekci hodnot:

my @values = 1, 2, 3;
for @values -> $value {
    say $value
}
# OUTPUT:
# 1
# 2
# 3

Raku hashe lze také přímo iterovat; tím se získají Pairobjekty klíč – hodnota . kvMetoda může být vyvolána na hash k iteraci přes klíče a hodnoty; keysmetoda iterovat přes klíčů hash je; a valuesmetoda iterace nad hodnotami hash.

my %word-to-number = 'one' => 1, 'two' => 2, 'three' => 3;
for %word-to-number -> $pair {
    say $pair;
}
# OUTPUT:
# three => 3
# one => 1
# two => 2

for %word-to-number.kv -> $key, $value {
    say "$key: $value" 
}
# OUTPUT:
# three: 3
# one: 1
# two: 2

for %word-to-number.keys -> $key {
    say "$key => " ~ %word-to-number{$key};
}
# OUTPUT:
# three => 3
# one => 1
# two => 2

Iterátory však lze použít a explicitně definovat. Pro jakýkoli iterovatelný typ existuje několik metod, které řídí různé aspekty procesu iterace. Například iteratormetoda má vrátit Iteratorobjekt a pull-onemetoda má produkovat a vrátit další hodnotu, pokud je to možné, nebo vrátit hodnotu sentinel, IterationEndpokud již nelze vytvořit další hodnoty. Následující příklad ukazuje ekvivalentní iteraci přes kolekci pomocí explicitních iterátorů:

my @values = 1, 2, 3;
my $it := @values.iterator;          # grab iterator for @values

loop {
    my $value := $it.pull-one;       # grab iteration's next value
    last if $value =:= IterationEnd; # stop if we reached iteration's end
    say $value;
}
# OUTPUT:
# 1
# 2
# 3

Všechny iterovatelné typy v Raku skládají Iterableroli, Iteratorroli nebo obojí. The Iterableje poměrně jednoduchý a vyžaduje pouze to, iteratoraby byl implementován skládací třídou. The Iteratoris more complex and provides a series of methods such as pull-one, which allows for a finer operation of iteration in several contexts such as adding or eliminating items, or skipping over them to access other items. Libovolná třída definovaná uživatelem tak může podporovat standardní iteraci skládáním těchto rolí a implementací metod iteratora / nebo pull-one.

DNATřída představuje řetězec DNA a provádí iteratorprostřednictvím skládání na Iterableroli. Řetězec DNA je při iteraci rozdělen na skupinu trinukleotidů:

subset Strand of Str where { .match(/^^ <[ACGT]>+ $$/) and .chars %% 3 };
class DNA does Iterable {
    has $.chain;
    method new(Strand:D $chain) {
        self.bless: :$chain
    }
 
    method iterator(DNA:D:){ $.chain.comb.rotor(3).iterator }
};

for DNA.new('GATTACATA') {
    .say
}
# OUTPUT:
# (G A T)
# (T A C)
# (A T A)

say DNA.new('GATTACATA').map(*.join).join('-');
# OUTPUT:
# GAT-TAC-ATA

Tyto Repeatertřídy komponuje obě Iterablea Iteratorrole:

class Repeater does Iterable does Iterator {
    has Any $.item  is required;
    has Int $.times is required;
    has Int $!count = 1;
    
    multi method new($item, $times) {
        self.bless: :$item, :$times;
    }
    
    method iterator { self }
    method pull-one(--> Mu){ 
        if $!count <= $!times {
            $!count += 1;
            return $!item
        }
        else {
            return IterationEnd
        }
    }
}

for Repeater.new("Hello", 3) {
    .say
}

# OUTPUT:
# Hello
# Hello
# Hello

Rubín

Ruby implementuje iterátory zcela odlišně; všechny iterace se provádějí pomocí předávání uzávěrů zpětného volání metodám kontejneru - tímto způsobem Ruby nejen implementuje základní iteraci, ale také několik vzorů iterací, jako je mapování funkcí, filtry a redukce. Ruby také podporuje alternativní syntaxi pro základní iterační metodu each, následující tři příklady jsou ekvivalentní:

(0...42).each do |n|
  puts n
end

...a...

for n in 0...42
  puts n
end

nebo dokonce kratší

42.times do |n|
  puts n
end

Ruby může také iterovat přes pevné seznamy pomocí Enumerators a buď zavolat jejich #nextmetodu nebo udělat pro každý z nich, jak je uvedeno výše.

Rez

S Rustem lze iterovat na elementu vektorů nebo vytvořit vlastní iterátory. Každý iterátor má adaptéry ( map, filter, skip, take, ...).

for n in 0..42 {
    println!("{}", n);
}

Pod fibonacci()funkcí vrací vlastní iterátor.

for i in fibonacci().skip(4).take(4) {
    println!("{}", i);
}

Viz také

Reference

externí odkazy