Vous devrez instancier tous les éléments de l'énumération pour pouvoir inverser leur ordre. Cela peut ne pas être possible. Considérez la séquence: IEnumerable<int> Infinity() { int i = 1; while (true) yield return i++; }comment inverseriez-vous cela?
Suncat2000 le
Réponses:
84
Lorsque vous travaillez avec une liste (indexation directe), vous ne pouvez pas le faire aussi efficacement qu'en utilisant une forboucle.
Edit: ce qui signifie généralement que lorsque vous êtes capable d'utiliser une forboucle, c'est probablement la bonne méthode pour cette tâche. De plus, pour autant qu'elle foreachsoit implémentée dans l'ordre, la construction elle-même est construite pour exprimer des boucles qui sont indépendantes des index des éléments et de l'ordre d'itération, ce qui est particulièrement important dans la programmation parallèle . À mon avis , l'itération reposant sur l'ordre ne devrait pas être utilisée foreachpour la boucle.
Je trouve votre dernière déclaration un peu trop générale. Il y a sûrement des cas où, par exemple, un IEnumerable d'un certain type doit être itéré dans l'ordre? N'utiliseriez-vous pas foreach dans ce cas? Que utiliseriez-vous?
avl_sweden
1
MISE À JOUR: Voir [la réponse de Bryan à une question similaire [( stackoverflow.com/a/3320924/199364 ), pour une réponse plus moderne, en utilisant Linq, et en discutant des deux listes, et d'autres énumérables.
ToolmakerSteve
MISE À JOUR: Jon Skeet a une réponse encore plus élégante, qui est maintenant possible, en utilisant " yield return".
ToolmakerSteve
2
J'ai eu la même réaction initiale que @avl_sweden - "l'itération reposant sur l'ordre ne devrait pas utiliser foreach" semble trop large. Oui, foreach est idéal pour exprimer des tâches parallèles indépendantes de l'ordre. MAIS c'est aussi une partie essentielle de la programmation moderne basée sur des itérateurs . Peut-être que le fait est qu'il serait plus clair / plus sûr s'il y avait deux mots - clés distincts , pour clarifier si l'on affirme que les itérations sont indépendantes de l'ordre? [Étant donné un langage comme Eiffel qui peut propager des contrats, de telles affirmations pourraient être prouvées vraies ou fausses, pour un code donné.]
ToolmakerSteve
2
L'idée que foreach "est construite pour exprimer des boucles indépendantes des index des éléments et de l'ordre d'itération" est incorrecte. La spécification du langage c # exige que chaque élément de processus soit dans l'ordre. Soit en utilisant MoveNext sur les itérateurs, soit en traitant les index en commençant à zéro et en incrémentant de un à chaque itération de tableaux.
Donald Rich
145
Si vous utilisez .NET 3.5, vous pouvez le faire:
IEnumerable<int> enumerableThing =...;foreach(var x in enumerableThing.Reverse())
Ce n'est pas très efficace car il doit passer par l'énumérateur avant de tout mettre sur une pile, puis tout ressortir dans l'ordre inverse.
Si vous avez une collection directement indexable (par exemple IList), vous devriez certainement utiliser une forboucle à la place.
Si vous êtes sur .NET 2.0 et que vous ne pouvez pas utiliser une boucle for (c'est-à-dire que vous n'avez qu'un IEnumerable), vous n'aurez qu'à écrire votre propre fonction Reverse. Cela devrait fonctionner:
Cela repose sur un comportement qui n'est peut-être pas si évident. Lorsque vous passez un IEnumerable au constructeur de pile, il l'itérera et poussera les éléments sur la pile. Lorsque vous parcourez ensuite la pile, les choses ressortent dans l'ordre inverse.
Ceci et la Reverse()méthode d'extension .NET 3.5 vont évidemment exploser si vous lui donnez un IEnumerable qui n'arrête jamais de renvoyer des éléments.
Aussi j'ai oublié de mentionner .net v2 seulement s'il vous plaît
JL.
4
Solution intéressante .NET 2.0.
RichardOD
11
Est-ce que je manque quelque chose ou votre solution .Net 3.5 ne fonctionne pas réellement? Reverse () inverse la liste en place et ne la renvoie pas. Dommage, car j'espérais une solution comme celle-là.
user12861
2
Certains administrateurs marquent cette réponse comme FAUX s'il vous plaît. Reverse () est un void [1] et donc l'exemple ci-dessus conduit à une erreur de compilation. [1] msdn.microsoft.com/en-us/library/b0axc2h2(v=vs.110).aspx
MickyD
6
@ user12861, Micky Duncan: la réponse n'est pas fausse, il vous manque quelque chose. Il existe une méthode Reverse sur System.Collections.Generic.List <T> qui effectue une inversion sur place. Dans .Net 3.5, il existe une méthode d'extension sur IEnumerable <T> nommée Reverse. J'ai changé l'exemple de var à IEnumerable <int> pour rendre cela plus explicite.
Matt Howells
55
Comme le dit 280Z28, IList<T>vous pouvez simplement utiliser l'index. Vous pouvez masquer cela dans une méthode d'extension:
publicstaticIEnumerable<T>FastReverse<T>(thisIList<T> items){for(int i = items.Count-1; i >=0; i--){yieldreturn items[i];}}
Ce sera plus rapide que celui Enumerable.Reverse()qui met en mémoire tampon toutes les données en premier. (Je ne crois pas Reversequ'aucune optimisation ne soit appliquée de cette manière Count().) Notez que cette mise en mémoire tampon signifie que les données sont lues complètement lorsque vous commencez à itérer, alors que vous FastReverse"verrez" toutes les modifications apportées à la liste pendant que vous itérez. (Il se cassera également si vous supprimez plusieurs éléments entre les itérations.)
Pour les séquences générales, il n'y a aucun moyen d'itérer en sens inverse - la séquence pourrait être infinie, par exemple:
publicstaticIEnumerable<T>GetStringsOfIncreasingSize(){string ret ="";while(true){yieldreturn ret;
ret = ret +"x";}}
À quoi vous attendriez-vous si vous essayiez de répéter cela en sens inverse?
Juste curiosité, pourquoi utiliser "> = 0" au lieu de "> -1"?
Chris S
15
> pourquoi utiliser "> = 0" au lieu de "> -1"? Parce que> = 0 communique mieux l'intention aux humains lisant le code. Le compilateur devrait être capable d'optimiser cela à l'équivalent> -1 si cela améliorerait les performances.
Fendre les bâtons 0 <= i est encore mieux. Après avoir enseigné les mathématiques à un certain nombre d'enfants au fil des ans et aidé les gens à coder, j'ai trouvé que cela réduisait considérablement les erreurs que les gens faisaient s'ils échangeaient TOUJOURS a> b en b <a car les choses se présentent alors dans l'ordre naturel d'une ligne. de nombres (ou l'axe X d'un système de coordonnées) - le seul inconvénient est si vous devez coller une équation dans XML, disons un .config
Eske Rahn
13
Avant d'utiliser foreachpour l'itération, inversez la liste par la reverseméthode:
myList.Reverse();foreach(List listItem in myList){Console.WriteLine(listItem);}
Un mot d'avertissement sur ce qui myListest sera utile. IEnumerable.Reverse ne fonctionnera pas ici.
nawfal le
2
@ MA-Maddin non les deux sont différents. Je suppose que le répondeur s'appuie sur List <T> .Reverse qui est en place.
nawfal
En fait, je ne sais pas pourquoi j'ai dit ça. J'aurais dû ajouter plus de détails ... Quoi qu'il en soit, c'est un code déroutant car il myListsemble être de type System.Collections.Generic.List<System.Windows.Documents.List>(ou de tout autre Listtype personnalisé ) sinon ce code ne fonctionne pas: P
Martin Schneider
5
Parfois, vous n'avez pas le luxe de l'indexation, ou peut-être voulez-vous inverser les résultats d'une requête Linq, ou peut-être que vous ne voulez pas modifier la collection source, si l'un de ces éléments est vrai, Linq peut vous aider.
Une méthode d'extension Linq utilisant des types anonymes avec Linq Select pour fournir une clé de tri pour Linq OrderByDescending;
var eable =new[]{"a","b","c"};foreach(var o in eable.Invert()){Console.WriteLine(o);}// "c", "b", "a"
Il est nommé "Invert" car il est synonyme de "Reverse" et permet de lever l'ambiguïté avec l'implémentation List Reverse.
Il est également possible d'inverser certaines plages d'une collection, puisque Int32.MinValue et Int32.MaxValue sont hors de la plage de tout type d'index de collection, nous pouvons les exploiter pour le processus de commande; si un index d'élément est inférieur à la plage donnée, il est affecté Int32.MaxValue afin que son ordre ne change pas lors de l'utilisation de OrderByDescending, de même, les éléments à un index supérieur à la plage donnée, seront affectés Int32.MinValue, de sorte qu'ils apparaissent à la fin du processus de commande. Tous les éléments de la plage donnée reçoivent leur index normal et sont inversés en conséquence.
publicstaticIEnumerable<T>Invert<T>(thisIEnumerable<T> source,int index,int count){var transform = source.Select((o, i)=>new{Index= i < index ?Int32.MaxValue: i >= index + count ?Int32.MinValue: i,Object= o
});return transform.OrderByDescending(o => o.Index).Select(o => o.Object);}
Usage:
var eable =new[]{"a","b","c","d"};foreach(var o in eable.Invert(1,2)){Console.WriteLine(o);}// "a", "c", "b", "d"
Je ne suis pas sûr des performances de ces implémentations Linq par rapport à l'utilisation d'une liste temporaire pour envelopper une collection pour l'inversion.
C'est possible si vous pouvez changer le code de collection qui implémente IEnumerable ou IEnumerable (par exemple votre propre implémentation de IList).
Créez un Iterator effectuant ce travail pour vous, par exemple comme l'implémentation suivante via l' interface IEnumerable (en supposant que 'items' est un champ List dans cet exemple):
publicIEnumerator<TObject>GetEnumerator(){for(var i = items.Count-1; i >=0; i--){yieldreturn items[i];}}IEnumeratorIEnumerable.GetEnumerator(){returnGetEnumerator();}
Pour cette raison, votre liste itérera dans l'ordre inverse dans votre liste.
Juste un indice: vous devez clairement indiquer ce comportement spécial de votre liste dans la documentation (encore mieux en choisissant un nom de classe explicite comme Stack ou Queue, aussi).
C'est mauvais car .Reverse()modifie en fait la liste.
Colin Basnett
-8
Cela fonctionne plutôt bien
List<string>list=newList<string>();list.Add("Hello");list.Add("Who");list.Add("Are");list.Add("You");foreach(String s inlist){Console.WriteLine(list[list.Count-list.IndexOf(s)-1]);}
IEnumerable<int> Infinity() { int i = 1; while (true) yield return i++; }
comment inverseriez-vous cela?Réponses:
Lorsque vous travaillez avec une liste (indexation directe), vous ne pouvez pas le faire aussi efficacement qu'en utilisant une
for
boucle.Edit: ce qui signifie généralement que lorsque vous êtes capable d'utiliser une
for
boucle, c'est probablement la bonne méthode pour cette tâche. De plus, pour autant qu'elleforeach
soit implémentée dans l'ordre, la construction elle-même est construite pour exprimer des boucles qui sont indépendantes des index des éléments et de l'ordre d'itération, ce qui est particulièrement important dans la programmation parallèle . À mon avis , l'itération reposant sur l'ordre ne devrait pas être utiliséeforeach
pour la boucle.la source
yield return
".Si vous utilisez .NET 3.5, vous pouvez le faire:
Ce n'est pas très efficace car il doit passer par l'énumérateur avant de tout mettre sur une pile, puis tout ressortir dans l'ordre inverse.
Si vous avez une collection directement indexable (par exemple IList), vous devriez certainement utiliser une
for
boucle à la place.Si vous êtes sur .NET 2.0 et que vous ne pouvez pas utiliser une boucle for (c'est-à-dire que vous n'avez qu'un IEnumerable), vous n'aurez qu'à écrire votre propre fonction Reverse. Cela devrait fonctionner:
Cela repose sur un comportement qui n'est peut-être pas si évident. Lorsque vous passez un IEnumerable au constructeur de pile, il l'itérera et poussera les éléments sur la pile. Lorsque vous parcourez ensuite la pile, les choses ressortent dans l'ordre inverse.
Ceci et la
Reverse()
méthode d'extension .NET 3.5 vont évidemment exploser si vous lui donnez un IEnumerable qui n'arrête jamais de renvoyer des éléments.la source
Comme le dit 280Z28,
IList<T>
vous pouvez simplement utiliser l'index. Vous pouvez masquer cela dans une méthode d'extension:Ce sera plus rapide que celui
Enumerable.Reverse()
qui met en mémoire tampon toutes les données en premier. (Je ne crois pasReverse
qu'aucune optimisation ne soit appliquée de cette manièreCount()
.) Notez que cette mise en mémoire tampon signifie que les données sont lues complètement lorsque vous commencez à itérer, alors que vousFastReverse
"verrez" toutes les modifications apportées à la liste pendant que vous itérez. (Il se cassera également si vous supprimez plusieurs éléments entre les itérations.)Pour les séquences générales, il n'y a aucun moyen d'itérer en sens inverse - la séquence pourrait être infinie, par exemple:
À quoi vous attendriez-vous si vous essayiez de répéter cela en sens inverse?
la source
Avant d'utiliser
foreach
pour l'itération, inversez la liste par lareverse
méthode:la source
myList
est sera utile. IEnumerable.Reverse ne fonctionnera pas ici.myList
semble être de typeSystem.Collections.Generic.List<System.Windows.Documents.List>
(ou de tout autreList
type personnalisé ) sinon ce code ne fonctionne pas: PParfois, vous n'avez pas le luxe de l'indexation, ou peut-être voulez-vous inverser les résultats d'une requête Linq, ou peut-être que vous ne voulez pas modifier la collection source, si l'un de ces éléments est vrai, Linq peut vous aider.
Une méthode d'extension Linq utilisant des types anonymes avec Linq Select pour fournir une clé de tri pour Linq OrderByDescending;
Usage:
Il est nommé "Invert" car il est synonyme de "Reverse" et permet de lever l'ambiguïté avec l'implémentation List Reverse.
Il est également possible d'inverser certaines plages d'une collection, puisque Int32.MinValue et Int32.MaxValue sont hors de la plage de tout type d'index de collection, nous pouvons les exploiter pour le processus de commande; si un index d'élément est inférieur à la plage donnée, il est affecté Int32.MaxValue afin que son ordre ne change pas lors de l'utilisation de OrderByDescending, de même, les éléments à un index supérieur à la plage donnée, seront affectés Int32.MinValue, de sorte qu'ils apparaissent à la fin du processus de commande. Tous les éléments de la plage donnée reçoivent leur index normal et sont inversés en conséquence.
Usage:
Je ne suis pas sûr des performances de ces implémentations Linq par rapport à l'utilisation d'une liste temporaire pour envelopper une collection pour l'inversion.
Au moment de la rédaction de cet article, je n'étais pas au courant de la propre implémentation inverse de Linq, mais c'était amusant de résoudre ce problème. https://msdn.microsoft.com/en-us/library/vstudio/bb358497(v=vs.100).aspx
la source
Si vous utilisez un List <T>, vous pouvez également utiliser ce code:
C'est une méthode qui écrit la liste inversée en elle-même.
Maintenant le foreach:
La sortie est:
la source
C'est possible si vous pouvez changer le code de collection qui implémente IEnumerable ou IEnumerable (par exemple votre propre implémentation de IList).
Créez un Iterator effectuant ce travail pour vous, par exemple comme l'implémentation suivante via l' interface IEnumerable (en supposant que 'items' est un champ List dans cet exemple):
Pour cette raison, votre liste itérera dans l'ordre inverse dans votre liste.
Juste un indice: vous devez clairement indiquer ce comportement spécial de votre liste dans la documentation (encore mieux en choisissant un nom de classe explicite comme Stack ou Queue, aussi).
la source
Non. ForEach parcourt simplement la collection pour chaque élément et l'ordre dépend s'il utilise IEnumerable ou GetEnumerator ().
la source
Élaborant légèrement sur la belle réponse de Jon Skeet , cela pourrait être polyvalent:
Et puis utilisez comme
la source
J'ai utilisé ce code qui a fonctionné
la source
.Reverse()
modifie en fait la liste.Cela fonctionne plutôt bien
la source