Article de reference

Itérateur

En programmation informatique , un itérateur est un objet qui permet d'accéder progressivement à chaque élément d'une collection , dans l'ordre. Une collection peut fournir plus...

programmation informatique , un itérateur est un objet qui permet d'accéder progressivement à chaque élément d'une collection , dans l'ordre.

Une collection peut fournir plusieurs itérateurs via son interface qui fournissent des éléments dans des ordres différents, tels que l'avant et l'arrière.

Un itérateur est souvent implémenté en fonction de la structure sous-jacente à une implémentation de collection et est souvent étroitement couplé à la collection pour permettre la sémantique opérationnelle de l'itérateur.

Un itérateur a un comportement similaire à celui d'un curseur de base de données .

Les itérateurs remontent au langage de programmation CLU en 1974.

compteur de boucle est parfois également appelé itérateur de boucle. Cependant, un compteur de boucle ne fournit que la fonctionnalité de parcours et non celle d'accès aux éléments.

Générateur

coroutine , appelée générateur . Contrairement à une sous-routine , une coroutine génératrice peut produire des valeurs à son appelant à plusieurs reprises, au lieu de ne retourner qu'une seule valeur. La plupart des itérateurs sont naturellement exprimables sous forme de générateurs, mais comme les générateurs préservent leur état local entre les appels, ils sont particulièrement adaptés aux itérateurs complexes avec état, tels que les parcours d'arbres . Il existe des différences et des distinctions subtiles dans l'utilisation des termes « générateur » et « itérateur », qui varient selon les auteurs et les langages. En Python , un générateur est un constructeur d'itérateur : une fonction qui retourne un itérateur. Voici un exemple de générateur Python retournant un itérateur pour les nombres de Fibonacciyield :

Generator[int, None, None]: 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) "
from typing import Generatordef fibonacci ( limit : int ) -> Generator [ int , None , None ]: a , b = 0 , 1 for _ in range ( limit ): yield a a , b = b , a + bpour nombre dans fibonacci ( 100 ): # Le générateur construit un itérateur print ( nombre )

itérateur interne

Un itérateur interne est une fonction d'ordre supérieur (prenant souvent des fonctions anonymes ) qui parcourt une collection en appliquant une fonction à chaque élément. Par exemple, la fonction Python `map` mapapplique une fonction définie par l'appelant à chaque élément :

C# , C++ (versions récentes), Delphi (versions récentes), Go , Java (versions récentes), Lua , Perl , Python et Ruby, offrent une méthode intrinsèque pour parcourir les éléments d'une collection sans itérateur explicite. Un objet itérateur peut exister, mais n'est pas représenté dans le code source.

Un itérateur implicite se manifeste souvent dans la syntaxe du langage sous la forme foreach.

En Python, un objet collection peut être itéré directement :

fonction anonyme ).

Les langages qui prennent en charge les compréhensions de listes ou des constructions similaires peuvent également utiliser des itérateurs implicites lors de la construction de la liste de résultats, comme en Python :

C++ propose quelques modèles de fonctions pour l'itération implicite, comme `for` for_each(). Ces fonctions requièrent toujours un objet itérateur explicite en entrée, mais l'itération suivante ne fournit aucun objet itérateur à l'utilisateur.

Flux

flux d'entrée : ils fournissent un objet potentiellement infiniment itérable (mais pas nécessairement indexable). Plusieurs langages, tels que Perl et Python, implémentent les flux sous forme d'itérateurs. En Python, les itérateurs sont des objets représentant des flux de données. D'autres implémentations de flux existent, notamment dans les langages orientés données , tels que AWK et sed .

Par contraste avec l'indexation

Au lieu d'utiliser un itérateur, de nombreux langages permettent d'utiliser un opérateur d'indexation et un compteur de boucle pour accéder à chaque élément. Bien que l'indexation puisse être utilisée avec les collections, l'utilisation d'itérateurs peut présenter des avantages tels que :

  • Les boucles de comptage ne conviennent pas à toutes les structures de données, en particulier aux structures de données sans accès aléatoire ou avec un accès aléatoire lent , comme les listes ou les arbres .
  • Les itérateurs peuvent fournir une méthode cohérente pour itérer sur des structures de données de toutes sortes, et rendent ainsi le code plus lisible, réutilisable et moins sensible à un changement dans la structure des données .
  • Un itérateur peut imposer des restrictions d'accès supplémentaires, par exemple en veillant à ce que les éléments ne puissent pas être ignorés ou qu'un élément déjà visité ne puisse pas être consulté une seconde fois.
  • Un itérateur permet de modifier l'objet collection sans être invalidé. Par exemple, une fois l'itérateur passé au-delà du premier élément, il est possible d'insérer des éléments supplémentaires au début de la collection avec des résultats prévisibles. Avec l'indexation, cela pose problème car les numéros d'index doivent être modifiés.

La possibilité de modifier une collection pendant son parcours est devenue essentielle en programmation orientée objet moderne , où les interrelations entre les objets et les effets des opérations peuvent être complexes. L'utilisation d'un itérateur permet de s'affranchir de ces conséquences. Toutefois, il convient de nuancer cette affirmation, car bien souvent, pour des raisons d'efficacité, l'implémentation de l'itérateur est si étroitement liée à la collection qu'elle empêche toute modification de cette dernière sans s'invalider elle-même.

Pour les collections dont les données peuvent être déplacées en mémoire, la seule façon de ne pas invalider l'itérateur est de maintenir, pour la collection, la trace de tous les itérateurs actifs et de les mettre à jour dynamiquement. Comme le nombre d'itérateurs à un instant donné peut être arbitrairement grand par rapport à la taille de la collection liée, leur mise à jour systématique compromet fortement la garantie de complexité des opérations de la collection.

Une autre façon de limiter le nombre de mises à jour à la taille de la collection consiste à utiliser un mécanisme de gestion des données, c'est-à-dire une collection de pointeurs indirects vers les éléments de la collection à mettre à jour. Les itérateurs pointent alors vers ces pointeurs plutôt que directement vers les éléments . Cependant, cette approche dégrade les performances des itérateurs, car elle nécessite un double accès aux données pour accéder à l'élément. Ceci est généralement à éviter, car de nombreux algorithmes utilisant des itérateurs font appel à l'accès aux données plus fréquemment qu'à la méthode d'avancement. Il est donc primordial d'utiliser des itérateurs offrant un accès aux données très efficace.

En définitive, il s'agit toujours d'un compromis entre sécurité (les itérateurs restent toujours valides) et efficacité. La plupart du temps, le gain en sécurité ne justifie pas la perte d'efficacité qu'il engendre. Utiliser une autre méthode de traitement (par exemple, une liste simplement chaînée au lieu d'un vecteur) serait un meilleur choix (globalement plus efficace) si la stabilité des itérateurs est essentielle.

Classification

Catégories

Les itérateurs peuvent être classés selon leur fonctionnalité. Voici une liste (non exhaustive) des catégories d'itérateurs :

CatégorieLangues
itérateur bidirectionnelC++ , Rust
Itérateur avantC++
itérateur d'entréeC++
itérateur de sortieC++
itérateur à accès aléatoireC++
Itérateur trivialC++ (ancienne STL )

Types

Différents langages ou bibliothèques utilisés avec ces langages définissent des types d'itérateurs. Certains d'entre eux sont

TaperLangues
itérateur de tableauPHP , R
itérateur de mise en cachePHP
itérateur constantC++ , PHP
itérateur de répertoirePHP, Python
itérateur de filtrePHP, R
itérateur limitePHP
itérateur de listeC# , Java , R
itérateur de tableau récursifPHP
itérateur XMLPHP

Dans différents langages de programmation

.FILET

Dans le framework .NET (C#), les itérateurs sont appelés « énumérateurs » et sont représentés par l' IEnumeratorinterface `Enumerator`. IEnumerator Cette interface fournit une MoveNext()méthode `next` qui passe à l'élément suivant et indique si la fin de la collection a été atteinte ; une Currentpropriété permettant d'obtenir la valeur de l'élément actuellement pointé ; et une Reset()méthode optionnelle permettant de rembobiner l'énumérateur à sa position initiale. L'énumérateur pointe initialement vers une valeur spéciale avant le premier élément ; un appel à `next` MoveNext()est donc nécessaire pour commencer l'itération.

Les énumérateurs sont généralement obtenus en appelant la GetEnumerator()méthode d'un objet implémentant l' IEnumerableinterface. une Currentpropriété, pour obtenir la valeur de l'élément actuellement pointé ; Les classes conteneurs implémentent généralement cette interface. Cependant, l' instruction foreach en C# peut opérer sur n'importe quel objet fournissant une telle méthode, même s'il ne l'implémente pas IEnumerable( typage dynamique ). Les deux interfaces ont été étendues en versions génériques dans .NET 2.0 .

L'exemple suivant illustre une utilisation simple des itérateurs en C# 2.0 :

les générateurs : une méthode déclarée comme retournant IEnumerator(ou IEnumerable), mais utilisant l' yield returninstruction " " pour produire une séquence d'éléments au lieu de retourner une instance d'objet, sera transformée par le compilateur en une nouvelle classe implémentant l'interface appropriée.

C

Les itérateurs n'existent pas nativement en C , mais peuvent être en quelque sorte émulés avec l'arithmétique des pointeurs .

L'exemple suivant illustre une liste chaînée intavec itérateur.

current) { return NULL; } int* value = &it->current->value; it->current = it->current->next; return value; } int main() { // ... for (LinkedListIterator it = list_begin(head); ; ) { int* val = list_next(&it); if (!val) { break; } printf(\"%d\ \", *val); } } "
typedef struct LinkedList { int * valeur ; struct LinkedList * suivant ; } LinkedList ;typedef struct { LinkedList * current ; } LinkedListIterator ;LinkedListIterator list_begin ( LinkedList * head ) { LinkedListIterator it = { head }; return it ; }int * list_next ( LinkedListIterator * it ) { if ( ! it -> current ) { return NULL ; } int * value = & it -> current -> value ; it -> current = it -> current -> next ; return value ; }int main () { // ... for ( LinkedListIterator it = list_begin ( head ); ) { int * val = list_next ( & it ) ; if ( ! val ) { break ; } printf ( "%d " , * val ); } }

C++

Le langage C++ utilise largement les itérateurs dans sa bibliothèque standard et décrit plusieurs catégories d'itérateurs, différant par l'ensemble des opérations qu'ils permettent. On trouve, par ordre croissant de possibilités, les itérateurs directs , les itérateurs bidirectionnels et les itérateurs à accès aléatoire . Tous les types modèles de conteneurs standard fournissent des itérateurs appartenant à l'une de ces catégories. Les itérateurs généralisent les pointeurs vers les éléments d'un tableau (qui peuvent effectivement servir d'itérateurs), et leur syntaxe est conçue pour ressembler à celle de l'arithmétique des pointeurs en C : les opérateurs ` &` et `&` permettent de référencer l'élément pointé par l'itérateur, tandis que les opérateurs d'arithmétique des pointeurs, tels que `&`, permettent de modifier les itérateurs lors du parcours d'un conteneur.*->++

Le parcours à l'aide d'itérateurs fait généralement intervenir un itérateur variable et deux itérateurs fixes servant à délimiter l'intervalle à parcourir. La distance entre les itérateurs limitatifs, exprimée en nombre d'applications de l'opérateur ++nécessaires pour transformer la limite inférieure en la limite supérieure, est égale au nombre d'éléments de l'intervalle ; le nombre de valeurs d'itérateur distinctes impliquées est supérieur d'une unité. Par convention, l'itérateur limitatif inférieur pointe vers le premier élément de l'intervalle, tandis que l'itérateur limitatif supérieur ne pointe vers aucun élément de l'intervalle, mais se situe juste au-delà de sa fin. Pour parcourir un conteneur entier, la begin()méthode fournit la limite inférieure et end()la limite supérieure. Cette dernière ne référence aucun élément du conteneur, mais constitue une valeur d'itérateur valide pouvant être comparée.

L'exemple suivant illustre une utilisation typique d'un itérateur.

, * it ); }// En utilisant les boucles foreach (ajoutées en C++11), // on peut faire la même chose sans utiliser d'itérateurs explicites : for ( int x : items ) { // Afficher la valeur de chaque élément 'x' de 'items'. std :: print ( "{}" , x ); }// Les deux boucles for affichent "529". }

Les types d'itérateurs sont distincts des types de conteneurs avec lesquels ils sont utilisés, bien que les deux soient souvent employés conjointement. La catégorie de l'itérateur (et donc les opérations qui lui sont associées) dépend généralement du type de conteneur. Par exemple, les tableaux et les vecteurs offrent des itérateurs à accès aléatoire, tandis que les ensembles (qui utilisent une structure chaînée comme implémentation) ne fournissent que des itérateurs bidirectionnels. Un même type de conteneur peut être associé à plusieurs types d'itérateurs. Par exemple, le std::vector<T>type de conteneur permet un parcours soit à l'aide de pointeurs (bruts) vers ses éléments (de type `int` T*), soit à l'aide de valeurs d'un type spécial std::vector<T>::iterator. Un autre type est fourni pour les « itérateurs inverses », dont les opérations sont définies de telle sorte qu'un algorithme effectuant un parcours classique (direct) effectuera en réalité un parcours en sens inverse lorsqu'il est appelé avec des itérateurs inverses. La plupart des conteneurs fournissent également un const_iteratortype distinct pour lequel les opérations permettant de modifier les valeurs pointées ne sont intentionnellement pas définies.

Le parcours simple d'un objet conteneur ou d'une plage de ses éléments (y compris la modification de ces éléments, sauf si un itérateur const_iteratorest utilisé) peut être effectué uniquement à l'aide d'itérateurs. Cependant, les types conteneurs peuvent également fournir des méthodes, telles que `map` insertou erase`filter`, qui modifient la structure du conteneur lui-même ; ces méthodes appartiennent à la classe du conteneur, mais requièrent en outre une ou plusieurs valeurs d'itérateur pour spécifier l'opération souhaitée. Bien qu'il soit possible d'avoir plusieurs itérateurs pointant simultanément vers le même conteneur, les opérations de modification de structure peuvent invalider certaines valeurs d'itérateur (la norme spécifie pour chaque cas si cela est possible) ; l'utilisation d'un itérateur invalide constitue une erreur qui entraînera un comportement indéfini , et de telles erreurs ne sont pas nécessairement signalées par le système d'exécution.

L'itération implicite est également partiellement prise en charge par C++ grâce à l'utilisation de modèles de fonctions standard, tels que std::for_each(), std::copy() et std::accumulate().

Lorsqu'ils sont utilisés, ils doivent être initialisés avec des itérateurs existants, généralement `map` beginet end`filter`, qui définissent la plage sur laquelle l'itération s'effectue. Cependant, aucun objet itérateur explicite n'est exposé par la suite au cours de l'itération. Cet exemple illustre l'utilisation de `map` for_each.

, i ); }// Boucle d'itération for-each. std::for_each(c.begin(), c.end ( ) , processItem ) ; // Utilisation de std :: ranges : std :: ranges :: for_each ( c , processItem );

On peut obtenir le même résultat en utilisant std::copy, en passant une std::ostream_iteratorvaleur comme troisième itérateur :

" ) ) ; // using std::ranges: std :: ranges :: copy ( c , ostream_iterator <ItemType> ( cout , " \ n " ) );

Depuis C++11 , la syntaxe des fonctions lambda permet de spécifier l'opération à itérer directement dans le code, évitant ainsi de définir une fonction nommée. Voici un exemple d'itération for-each utilisant une fonction lambda :

, i ) ; });// Utilisation de std::ranges : std :: ranges :: for_each ( c , []( const ItemType & i ) -> void { std :: println ( "{}" , i ); });

La syntaxe de style Java pour les itérateurs ( Iterator<T>, vs T::iterator) peut être implémentée en C++ de la manière suivante :

{ }" , * it ) ; } }

Java

Introduite dans la version 1.2 du JDK Java, " ) ; } }

Pour montrer que cette fonction hasNext()peut être appelée à plusieurs reprises, nous l'utilisons pour insérer des virgules entre les éléments, mais pas après le dernier élément.

Cette approche ne dissocie pas correctement l'opération d'avance de l'accès aux données proprement dit. Si l'élément de données doit être utilisé plusieurs fois pour chaque avance, il est nécessaire de le stocker dans une variable temporaire . Lorsqu'une avance est requise sans accès aux données (c'est-à-dire pour ignorer un élément de données donné), l'accès est néanmoins effectué, même si la valeur renvoyée est ignorée dans ce cas.

Pour les types de collections compatibles, la remove()méthode de l'itérateur supprime l'élément le plus récemment visité du conteneur tout en conservant l'itérateur utilisable. L'ajout ou la suppression d'éléments via les méthodes du conteneur (également depuis le même thread ) rend l'itérateur inutilisable. Toute tentative d'accès à l'élément suivant lève une exception. Une exception est également levée s'il ne reste plus d'éléments ( hasNext()la méthode ayant précédemment renvoyé `false`).

De plus, J2SE 5.0 de Java a introduit l' foreach ) améliorée pour itérer sur les collections et les tableaux. IterableElle définit la Scala , les itérateurs disposent d'un large éventail de méthodes similaires aux collections et peuvent être utilisés directement dans les boucles `for`. En effet, itérateurs et collections héritent d'un trait de base commun scala.collection.TraversableOnce. Cependant, grâce à la richesse des méthodes disponibles dans la bibliothèque de collections de Scala, telles que map`map`, `filter` collect, filteretc., il est souvent inutile de manipuler directement les itérateurs lors de la programmation en Scala.

Les itérateurs et collections Java peuvent être automatiquement convertis en itérateurs et collections Scala, respectivement, en ajoutant simplement une ligne de code.

MATLAB prend en charge l'itération implicite externe et interne, utilisant soit des tableaux natifs, soit celldes tableaux de type tableau. Dans le cas de l'itération externe, où il incombe à l'utilisateur de faire progresser le parcours et de demander les éléments suivants, il est possible de définir un ensemble d'éléments dans une structure de stockage de type tableau et de parcourir ces éléments à l'aide de la forboucle `for`. Par exemple :

type de données abstrait List (ADT) pour stocker un ensemble d'éléments hétérogènes (en termes de types de données). Elle offre la possibilité de parcourir explicitement la liste en avant grâce aux méthodes `for` et `for` hasNext(), utilisables dans une boucle `for`.next()reset()while

PHP

Diagramme de classes UML de l'interface Iterator en PHP
Diagramme de classes UML de l'interface Iterator en PHP

foreachLa boucle `for` de PHP a été introduite dans la version 4.0 et rendue compatible avec les objets comme valeurs dans la version bêta 4.0. Cependant, la prise en charge des itérateurs a été ajoutée dans PHP 5 grâce à l'introduction de l'interface interne `Iterator` Traversable . Les deux principales interfaces permettant d'itérer sur des objets via une foreachboucle dans les scripts PHP sont ` Iterator` Iteratoret `Générateur` IteratorAggregate. Cette dernière ne requiert pas que la classe implémentant l'itération déclare toutes les méthodes nécessaires ; elle implémente plutôt une méthode d'accèsgetIterator ` get` qui renvoie une instance de ` Iterator` Traversable. La bibliothèque standard PHP (SPL ) fournit plusieurs classes pour manipuler des itérateurs spécifiques. PHP prend également en charge les générateurs depuis la version 5.5.

L'implémentation la plus simple consiste à encapsuler un tableau, ce qui peut être utile pour l'indication de type et la dissimulation d'informations .

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; } } "
espace de noms Wikipedia\Itérateur ;classe finale ArrayIterator étend \Iterator { tableau privé $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 "clé : { $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 'valide : ' , ( $valid ? 'true' : 'false' ), PHP_EOL ; return $valid ; } }

Toutes les méthodes de la classe d'exemple sont utilisées lors de l'exécution d'une boucle foreach complète ( foreach ($iterator as $key => $current) {}). Les méthodes de l'itérateur sont exécutées dans l'ordre suivant :

  1. $iterator->rewind()garantit que la structure interne est mise en place dès le début.
  2. $iterator->valid()Renvoie vrai dans cet exemple.
  3. $iterator->current()La valeur renvoyée est stockée dans $value.
  4. $iterator->key()La valeur renvoyée est stockée dans $key.
  5. $iterator->next()passe à l'élément suivant de la structure interne.
  6. $iterator->valid()renvoie faux et la boucle est interrompue.

L'exemple suivant illustre une classe PHP implémentant l' Traversableinterface, qui pourrait être encapsulée dans une autre IteratorIteratorclasse pour traiter les données avant leur retour dans la foreachboucle. L'utilisation de cette interface, combinée à la MYSQLI_USE_RESULTconstante, permet aux scripts PHP de parcourir des ensembles de résultats contenant des milliards de lignes avec une consommation de mémoire minimale. Ces fonctionnalités ne sont pas exclusives à PHP ni à ses implémentations de classes MySQL (par exemple, la PDOStatementclasse implémente Traversableégalement l'interface).

query('SELECT `a`, `b`, `c` FROM `table`', MYSQLI_USE_RESULT) as $row) { // Act on the returned row, which is an associative array. } "
mysqli_report ( MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT ); $mysqli = new \mysqli ( 'host.example.com' , 'username' , 'password' , 'database_name' );// La classe \mysqli_result renvoyée par l'appel de méthode implémente l'interface interne Traversable. foreach ( $mysqli -> query ( 'SELECT `a`, `b`, `c` FROM `table`' , MYSQLI_USE_RESULT ) as $row ) { // Traiter la ligne renvoyée, qui est un tableau associatif. }

Python

Les itérateurs en Python sont une composante fondamentale du langage et, dans de nombreux cas, restent invisibles car ils sont utilisés implicitement dans l' instruction for` foreach` , les compréhensions de listes et les expressions génératrices . Tous les types de collections intégrés standard de Python prennent en charge l'itération, ainsi que de nombreuses classes de la bibliothèque standard. L'exemple suivant illustre une itération implicite typique sur une séquence :

tableau associatif ) peuvent également être parcourus directement, lorsque les clés du dictionnaire sont renvoyées ; ou bien items()on peut parcourir la méthode d’un dictionnaire qui renvoie les paires clé-valeur correspondantes sous forme de tuple :

Les générateurs de Python implémentent ce protocole d'itération .

Raku

Les itérateurs sont un élément fondamental du langage Raku , même si les utilisateurs n'ont généralement pas à s'en préoccuper. Leur utilisation est masquée par des API d'itération telles que l' forinstruction `map` map, ` grepfilter`, l'indexation de listes avec `map` .[$idx], etc.

L'exemple suivant illustre une itération implicite typique sur une collection de valeurs :

$value { say $value } # OUTPUT: # 1 # 2 # 3 "
mes @values ​​= 1 , 2 , 3 ; pour @values ​​-> $value { dire $value } # SORTIE : # 1 # 2 # 3

Les tables de hachage Raku peuvent également être parcourues directement, ce qui produit Pairdes objets clé-valeur. La kvméthode `getKeys` permet de parcourir les clés et les valeurs de la table ; la keysméthode `getKeys` permet de parcourir les clés ; et la valuesméthode `getValues` permet de parcourir les valeurs.

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 "
mon %mot-à-nombre = 'un' => 1 , 'deux' => 2 , 'trois' => 3 ; pour %mot-à-nombre -> $pair { dire $pair ; } # SORTIE : # trois => 3 # un => 1 # deux => 2pour %word-to-number . kv -> $key , $value { dire "$key: $value" } # SORTIE : # trois : 3 # un : 1 # deux : 2pour %word-to-number . clés -> $key { dire "$key => " ~ %word-to-number { $key }; } # SORTIE : # trois => 3 # un => 1 # deux => 2

Les itérateurs peuvent toutefois être utilisés et définis explicitement. Pour tout type itérable, plusieurs méthodes permettent de contrôler différents aspects du processus d'itération. Par exemple, la iteratorméthode `next` doit renvoyer un Iteratorobjet, et la pull-oneméthode `next` doit produire et renvoyer la valeur suivante si possible, ou renvoyer la valeur sentinelleIterationEnd si aucune autre valeur ne peut être produite. L'exemple suivant illustre une itération équivalente sur une collection à l'aide d'itérateurs explicites :

" , 3 ) { . dire } # SORTIE : # Bonjour # Bonjour # Bonjour

Rubis

Ruby implémente les itérateurs de manière assez différente ; toutes les itérations sont effectuées en passant des fonctions de rappel (callbacks) aux méthodes conteneurs. Ainsi, Ruby implémente non seulement l'itération de base, mais aussi plusieurs modèles d'itération comme le mappage de fonctions, les filtres et la réduction. Ruby prend également en charge une syntaxe alternative pour la méthode d'itération de baseeach ; les trois exemples suivants sont équivalents :

, i ); } // Affiche les nombres de 0 à 41

Plus précisément, la forboucle appelle la into_iter()méthode d'une valeur, laquelle renvoie un itérateur qui, à son tour, transmet les éléments à la boucle. La forboucle (ou toute méthode consommant l'itérateur) se poursuit jusqu'à ce que la next()méthode renvoie une Nonevaleur (les itérations produisant des éléments renvoient une Some(T)valeur, où Test le type de l'élément).

Toutes les collections fournies par la bibliothèque standard implémentent le IntoIteratortrait (c'est-à-dire qu'elles définissent la into_iter()méthode). Les itérateurs eux-mêmes implémentent ce Iteratortrait, ce qui nécessite la définition de la next()méthode. De plus, tout type implémentant ce trait Iteratorse voit automatiquement fournir une implémentation de IntoIteratorcette méthode qui renvoie l'itérateur lui-même.

Les itérateurs prennent en charge divers adaptateurs ( map(), filter(), skip(), take(), etc.) en tant que méthodes fournies automatiquement par le Iteratortrait.

Les utilisateurs peuvent créer des itérateurs personnalisés en créant un type implémentant le Iteratortrait. Les collections personnalisées peuvent implémenter ce IntoIteratortrait et renvoyer un type d'itérateur associé à leurs éléments, permettant ainsi leur utilisation directe dans forles boucles. Ci-dessous, le Fibonaccitype implémente un itérateur personnalisé non borné :

Self { Self(0, 1) } } impl Iterator for Fibonacci { type Item = u64; fn next(&mut self) -> Option { let next = self.0; self.0 = self.1; self.1 = self.0 + next; Some(next) } } fn main() { let fib = Fibonacci::new(); for n in fib.skip(1).step_by(2).take(4) { println!(\"{n}\"); } // Prints 1, 2, 5, and 13 } "
struct Fibonacci ( u64 , u64 );impl Fibonacci { pub fn new () -> Self { Self ( 0 , 1 ) } }impl Itérateur pour Fibonacci { type Item = u64 ;fn next ( & mut self ) - > Option < Self :: Item > { let next = self.0 ; self.0 = self.1 ; self.1 = self.0 + next ;Certains ( suivant ) } }fn main () { let fib = Fibonacci :: new ( ); for n in fib.skip ( 1 ) .step_by ( 2 ) .take ( 4 ) { println! ( " { n}" ); } // Affiche 1, 2, 5 et 13 }

Diagramme de classes UML représentant les structures et les traits liés aux itérateurs en Rust

Plus d articles de Worldlex Wiki

Revenez a l index pour explorer davantage de pages sur l histoire, la science, la culture, la geographie et la societe en francais.

Explorer l index