J'ai trouvé un sujet similaire:
itérateurs en C ++ (stl) vs java, est Il y a une différence conceptuelle?
qui traite essentiellement de Java Itérateur (qui est similaire à C #) étant incapable de monter en arrière. p>
donc je voudrais me concentrer sur les limites - C ++ Itérateur ne connaît pas sa limite, vous avez par vous-même comparer l'itérateur donné avec la limite. En C # Itérateur en sait plus - vous pouvez le dire sans comparer à aucune référence externe, si l'itérateur est valide ou non. P>
i préférez C ++ moyen, car une fois que vous avez un itérateur, vous pouvez définir tout itérateur comme une limite. En d'autres termes si vous souhaitez obtenir seulement quelques éléments à la place de la collection complète, vous n'avez pas à modifier l'itérateur (en C ++). Pour moi, il est plus "pur" (clair). P>
mais bien sûr que MS connaissait ceci et C ++ tout en concevant C #. Alors, quels sont les avantages de la manière C #? Quelle approche est plus puissante (ce qui conduit à des fonctions plus élégantes basées sur des itérateurs). Qu'est-ce que je manque? P>
Si vous avez des pensées sur c # vs c ++ itérateurs Note Strong>: (Juste au cas où) s'il vous plaît, gardez la discussion strictement technique. No C ++ / C # Flamewar. P> Comme Tzaman l'a dit "Il n'y a aucun avantage à avoir la limite d'être exprimée séparément, car il n'y a aucun moyen d'y arriver, sauf de marcher à l'exception d'un élément à un autre temps." Cependant, il n'est pas si difficile de construire un itérateur C # qui fait plusieurs étapes un temps, donc la question - est-il un avantage d'avoir une limite explicite Itérateur (comme en C ++)? Si oui - quoi? P> Edit1: EM> Disons que vous avez une fonction FOO qui fait quelque chose sur les itérateurs (l'exemple est très naïf !) p> et maintenant, vous souhaitez appeler la barre de fonction sur tous les éléments, à l'exception des 10 derniers. p> in c # (Si je ne me trompe pas), vous devriez fournir une méthode supplémentaire à cet itérateur de changer sa limite em>, quelque chose comme ceci: p> Edits h1>
@jon, H2>
sort(IEnumerator<T> iter)
5 Réponses :
Il me semble que la conception C # est plus encapsulée: une séquence s'épuise ou fait, indépendante de toute autre chose. Où cela fait-il sens em> pour comparer une limite avec une autre? Si vous ne voulez que prendre quelques éléments, alors LINQ fournit un nombre de façons de construire une séquence d'une autre, par exemple P > Bar(list.Take(list.Count - 10))
Ouais ... Cela semble certainement plus gentil, mais qu'est-ce qu'ils nous attendaient à faire avant i> Linq?
Merci pour votre réponse. Dans votre exemple et ci-dessous, vous avez supposé que votre "entrée" est une collection. Mais si c'est déjà un itérateur? Étant donné que les commentaires ne forment pas bien, je modifie mon message.
@Mark: en C # 1, il aurait été assez moche de le faire. En C # 2, vous pouvez construire votre propre pseudo-linq raisonnablement facilement à l'aide de blocs d'itérateurs - il manquerait cependant la concision des expressions et des méthodes d'extension Lambda.
@Jon, je sais qu'il ne s'agit pas de modifier les éléments de collecte :-) Maintenant, à propos de l'exemple - vous ne pouvez pas compter sur le fait que vous recevez gratuitement, par exemple pour la liste liée, c'est O (n) pour l'obtenir, donc C ++ dans ce cas serait beaucoup plus rapide. Et en plus, vous avez encore une fois utilisé Collection i> au lieu de Itérateur i>. Donc, je suis de plus en plus curieux - si vraiment C ++ Itérateurs sont des collections C # Collections (non itérateurs) contrepartie? Ou peut-être que je repasse ma question - C ++ STL Fonctions générales (#include
@Macias: vous obtenez le compte pour O (1) dans un .net LinkedList
Une expression comme liste.end () - n est O (n) dans une liste STD :: Liste également. Et O (1) pour un STD :: Vecteur, juste comme une liste <>. Aucune différence là-bas.
@Jon, donc, c # stl homologue serait basé sur des séquences et non des itérateurs. Ok, c'est un quart de travail pour ma pensée :-) merci. @Hans, bon point. Les étapes exactes diffèrent cependant.
Alors, quels sont les avantages de la manière C #? p>
encapsulation pour un. Il rend plus difficile de gâcher vos limites d'itération si vous ne définissez pas la séquence d'itération manuellement. P>
C ++ Exemple: P>
std::vector<int> a; std::vector<int> b; std::vector<int>::iterator ba = a.begin(); std::vector<int>::iterator ea = a.end(); std::vector<int>::iterator bb = b.begin(); std::vector<int>::iterator eb = b.end(); // lots of code here for(auto itr = ba; itr != eb; itr++) // iterator tries to "cross" vectors // you get undefined behavior, depending // on what you do with itr
Votre exemple est très convaincant, merci pour ça! Puissant = Vous pouvez exprimer la plupart des algorithmes (trier, partition, trouver, tout, tout etc.) de manière claire.
@MACIAS: Vous pouvez exprimer une partition, trouver, toutes les séquences C # (et incroyablement facilement, en utilisant des expressions Lambda).
Pour cette définition, les itérateurs C ++ sont définitivement plus puissants. Vous avez des itérateurs d'E / S, spécifiant des gammes personnalisées pour algorithmes (`std :: tr tri (c.begin (), c.begin (), c.begin () + 3)`), utilisez les mêmes algorithmes avec des itérateurs et des point-points cru I>) Et ainsi de suite.
Je pense aussi longtemps que vous ne parlez que sur avance em> itérateurs, j'aime la solution C # - la séquence connaît sa propre limite, vous pouvez le transformer facilement via Linq, etc. Il n'y a aucun avantage à avoir la limite d'être exprimée séparément, car il n'y a aucun moyen de y arriver em> sauf marcher un élément à la fois. p>
Cependant, les itérateurs C ++ sont beaucoup plus puissants - vous pouvez avoir des itérateurs bidirectionnels, ou des itérateurs aléatoires, qui vous permettent de faire des choses qui ne sont pas possibles avec un itérateur séquentiel à sens unique. Un itérateur séquentiel est dans un sens une généralisation d'une liste liée EM>, tandis qu'un itérateur d'accès aléatoire est une généralisation d'un tableau (em>. C'est pourquoi vous pouvez exprimer efficacement des algorithmes comme Quicksort en termes d'itérateurs C ++, tandis que cela serait impossible avec un ienumerable code>. p>
Oh, j'aime votre réponse - cependant pourriez-vous s'il vous plaît expliquer un peu la dernière déclaration. Ou postez un lien vers un ext. référence. À propos des différents types d'itérateurs - on ne nie pas l'autre, c'est-à-dire que vous pourriez avoir c # itérateur (avec limite), mais bidirectionnel.
Pour "Itérateur d'accès aléatoire", vous utilisez simplement ilist
@Jon - bien sûr; Toujours un ilist
/ Etc. Toute supposition de pourquoi il n'y a pas de fichier
deque
ibidirectionnel code>? Ils sont i> utiles ...
@tzman: Dans de nombreux cas, vous pouvez simplement utiliser un linkedlist
deque
Y a-t-il une raison pour que ienumerator
int bouge (int); code> méthode qui, pour une valeur non négative, essayerait d'appeler
MOVENNEXT CODE> Le nombre de fois spécifié et renvoie 0 s'il pourrait déplacer le montant total, ou la quantité de déficit si elle ne pouvait pas (la dernière quantité étant utile si plusieurs
ienumerables
ienumerator
movenext () code> si rien d'autre, mais de nombreuses implémentations pourraient utiliser des approches alternatives d'approches de grandeur plus rapidement que les appels répétés vers
MOVENEXT code>.
@supercat Il y a, c'est ce qu'on appelle saute code>. Je suis à peu près sûr qu'il est spécialisé d'être
O (1) Code> pour la liste
sous-jacente CODE> Types, mais je n'ai pas vérifié.
@Tzaman: la méthode d'extension code> Ignorer code>, comme Nombre code>, est spécialisé pour être O (1) pour quelques cas spéciaux, mais ces spécialisations échouent totalement si par exemple. On utilise la méthode d'extension code> concat code> pour joindre un itérateur à dix éléments à la fin d'une liste d'articles. Par contraste, si
ienumerator code> avait une méthode
(int) code>, et on a cherché à déplacer 1 000 005 articles à travers l'énumérateur produit par la concaténation susmentionnée, elle pourrait alors demander au
Liste
@Tzaman: une telle approche ne s'appuierait pas sur des spécialisations spécifiques à un type et serait en mesure de traiter efficacement les agrégations composites comme celles produites par concat code> - quelque chose que les spécialisations ne seraient pas en mesure de gérer parce qu'ils ne peuvent pas voir à l'intérieur de ces agrégations.
Vous comparez des pommes et des oranges. Les classes de collection STL avaient des objectifs de conception complètement différents. Ce sont une conception unique des collections, vous ne pouvez jamais recevoir les classes fournies. Il n'y a aucun moyen de personnaliser une collection, les classes STL n'ont pas été conçues pour être héritées de. P>
Très différent des .NET, leur noyau est l'ICOLLECTION, les interfaces ILLITION ET ILIST. Le .NET Framework contient des centaines de classes de collecte, personnalisées pour obtenir leur travail spécifique. P>
qui affectait également les conceptions de leurs itérateurs. Les itérateurs STL doivent EM> fournir beaucoup de flexibilité puisque les collections qu'elles itérations ne peuvent pas être personnalisées. Il y a un coût inhérent associé à cela. Il existe 5 types d'itérateurs STL différents, toutes les classes de collecte ne prennent pas en charge les 5 types différents. La perte de généralité n'est jamais une bonne chose dans une conception de la bibliothèque. P>
Un argument peut être fait que les itérateurs C ++ sont plus flexibles, je vais faire l'affirmation opposée. La simplicité d'Ienumerator était un catalyseur pour de très précieuses fonctionnalités .NET. Le mot clé C # rendement par exemple, il dissocie complètement une collection avec le comportement d'un itérateur. LINQ n'aurait pas eu lieu si ce n'était pas pour un simple type d'itérateur. Le soutien de la covariance n'aurait pas été possible. P>
En ce qui concerne votre edit2 em>, non, les itérateurs .NET sont des cours distincts, tout comme les C ++. Assurez-vous de comprendre la différence entre iEnumerable (implémenté par la collection) et ienumerator (implémenté par l'itérateur). P>
Ok, beaucoup ira à éditer :-) Je pense que vous me faites convaincre d'un exemple de rendement. C'est à dire. Avec deux itérateurs pour définir les limites que vous devez savoir à l'avance la limite (la limite peut changer, mais elle doit être là). Donc, la contrepartie de rendement devrait transférer deux valeurs à chaque fois, ce qui signifie que l'itérateur actuel devrait connaître sa limite -> qui nous conduit à C # Itérateur. Joli.
Je pose respectueusement que c'est une mauvaise réponse. Premièrement, vous êtes factuellement incorrect sur les collections et les itérateurs STL: bien sûr, ils peuvent être hérités et personnalisés. La STL est conçue pour séparer les algorithmes de la structure - c'est-à-dire le std :: copier code> ou
std :: for_ach code> Les fonctions ne doivent pas avoir à se soucier de ses arguments pointant vers, seulement qu'ils sont des itérateurs valides. Vous avez également tort de C #: il n'y a pas d'itérateurs, seulement des collections.
Bon, d'accord. Je devrais clarifier: les interfaces getenumerator code> qui renvoie un objet implémentant
ienumerator code>, qui expose une méthode appelée
movenext code >. Cependant, cet énumérateur n'est pas vraiment la même chose qu'un itérateur.
J'ai trouvé ce sujet à cause du problème suivant. Il y a une séquence qui doit être avancée d'un élément à la fois. Cependant, à certains points, un grand nombre d'éléments doivent être ignorés. Lorsque de tels sauts se produisent, il est inefficace de faire tout ce que sauter et redémarrer à la nouvelle index (c'est-à-dire qu'il ne s'agit pas d'exécuter la boucle pour les indices sautés, même sans une exécution de corps en boucle.)
Voici un extrait de C # à donner vous l'idée, sauf que c'est à l'aide d'un index INT plutôt que d'un énumérateur. p> Le point est que cela se passe à l'élément suivant peut être optimisé pour cette séquence particulière, mais Sauter des éléments (et un accès aléatoire complet) est plus coûteux. @Jon, en utilisant IList est donc inefficace à cette fin. Le rendement ne permet pas directement à ce style. P> J'ai essayé de différentes façons de le faire en C #, y compris à l'aide d'un style Lambda Foreach () pour le bloc interne, qui renvoie l'index suivant. Cependant, cela ne fonctionne pas très bien. P> L'approche résultera probablement du moins de frais généraux est un type d'itérateur simple - non basé sur iEnumérable ou donné - cela me permet d'imiter le code ci-dessus. p> pensées? p> p>
@macias, comme n'étant ni un développeur C ++ ou C #, je préfère le style C # dans votre exemple. Cela me semble plus naturel.
Comment les séquences infinies sont-elles traitées dans C ++? Qu'est-ce que iter_end serait dans de telles circonstances?
@Damien, je n'ai jamais travaillé avec une séquence infinitive en C ++.
Deux mots: principe de responsabilité.
@Damien: Pour les séquences infinies,
iter_end code> doit simplement être une valeur spéciale qui ne sera jamais atteinte (par exemple, un iStream_iterator peut désigner une plage infinie si le flux ne finit jamais). Toutefois, si un algorithme est conçu pour fonctionner avec une séquence infinie, il ne devrait probablement prendre qu'un seul itérateur. Par exemple, l'algorithme
std :: copie code> prend deux itérateurs d'entrée indiquant la plage source (ou la séquence), mais un seul itérateur de sortie, qui peut être plus ou moins vu comme une plage infinie où la copie sera être fait. [...]
[...] Les itérateurs qui génèrent des valeurs peuvent également être considérés comme des itérateurs sur une séquence infinie. Je pense que c'est à Boost, ils appellent cela une "séquence paresseuse" (voir par exemple boost :: comptage_iterator, qui génère des valeurs incrémentielles).