Möglich, durch einen Foreach rückwärts zu iterieren?
129
Ich weiß, dass ich eine forAnweisung verwenden und den gleichen Effekt erzielen kann, aber kann ich eine foreachSchleife in C # rückwärts durchlaufen ?
Sie müssten alle Elemente in der Aufzählung instanziieren, damit Sie ihre Reihenfolge umkehren können. Es ist möglicherweise nicht möglich. Betrachten Sie die Reihenfolge: IEnumerable<int> Infinity() { int i = 1; while (true) yield return i++; }Wie würden Sie das umkehren?
Suncat2000
Antworten:
84
Wenn Sie mit einer Liste arbeiten (direkte Indizierung), können Sie dies nicht so effizient tun wie mit einer forSchleife.
Edit: Was bedeutet in der Regel, wenn Sie sind die Lage , einen verwenden , forSchleife, ist es wahrscheinlich die richtige Methode für diese Aufgabe. Darüber hinaus foreachist das Konstrukt selbst so weit wie in der Reihenfolge implementiert implementiert, um Schleifen auszudrücken, die unabhängig von Elementindizes und Iterationsreihenfolge sind, was bei der parallelen Programmierung besonders wichtig ist . Es ist meiner Meinung nach, dass Iteration auf Bestellung verlassen sollte nicht verwendet werden foreachfür Looping.
Ich finde deine letzte Aussage etwas zu allgemein. Sicherlich gibt es Fälle, in denen zum Beispiel eine IEnumerable in irgendeiner Reihenfolge durchlaufen werden muss? Würden Sie in diesem Fall nicht foreach verwenden? Was würdest du verwenden?
avl_sweden
1
UPDATE: Siehe [Bryans Antwort auf eine ähnliche Frage [( stackoverflow.com/a/3320924/199364 )] für eine modernere Antwort unter Verwendung von Linq und Erörterung beider Listen und anderer Aufzählungen.
ToolmakerSteve
UPDATE: Jon Skeet hat eine noch elegantere Antwort, die jetzt mit " yield return" möglich ist .
ToolmakerSteve
2
Ich hatte die gleiche anfängliche Reaktion wie @avl_sweden - "Iteration, die sich auf die Reihenfolge stützt, sollte nicht für jeden verwendet werden" klingt zu breit. Ja, foreach ist gut geeignet, um auftragsunabhängige parallele Aufgaben auszudrücken. ABER es ist auch ein wesentlicher Bestandteil der modernen iteratorbasierten Programmierung . Vielleicht ist der Punkt, dass es klarer / sicherer wäre, wenn es zwei unterschiedliche Schlüsselwörter gäbe , um zu klären, ob man behauptet, dass die Iterationen auftragsunabhängig sind? [Bei einer Sprache wie Eiffel, die Verträge verbreiten kann, können solche Behauptungen für den gegebenen Code nachweislich wahr oder falsch sein.]
ToolmakerSteve
2
Die Idee, dass foreach "zum Ausdrücken von Schleifen erstellt wurde, die unabhängig von Elementindizes und Iterationsreihenfolge sind", ist falsch. Die c # -Sprachenspezifikation erfordert, dass alle Prozesselemente in der richtigen Reihenfolge angezeigt werden. Entweder durch Verwendung von MoveNext für Iteratoren oder durch Verarbeitung von Indizes, die bei Null beginnen und bei jeder Iteration von Arrays um eins erhöht werden.
Donald Rich
145
Wenn Sie mit .NET 3.5 arbeiten, können Sie Folgendes tun:
IEnumerable<int> enumerableThing =...;foreach(var x in enumerableThing.Reverse())
Es ist nicht sehr effizient, da es im Grunde genommen den Enumerator durchlaufen muss, um alles auf einen Stapel zu legen, und dann alles in umgekehrter Reihenfolge wieder herausspringt.
Wenn Sie eine direkt indizierbare Sammlung haben (z. B. IList), sollten Sie forstattdessen auf jeden Fall eine Schleife verwenden.
Wenn Sie sich in .NET 2.0 befinden und keine for-Schleife verwenden können (dh Sie haben nur eine IEnumerable), müssen Sie nur Ihre eigene Umkehrfunktion schreiben. Das sollte funktionieren:
Dies beruht auf einem Verhalten, das vielleicht nicht so offensichtlich ist. Wenn Sie eine IEnumerable an den Stapelkonstruktor übergeben, durchläuft sie diese und schiebt die Elemente auf den Stapel. Wenn Sie dann durch den Stapel iterieren, werden die Dinge in umgekehrter Reihenfolge wieder herausgeschleudert.
Diese und die .NET 3.5- Reverse()Erweiterungsmethode werden offensichtlich in die Luft gesprengt, wenn Sie eine IEnumerable eingeben, die niemals aufhört, Elemente zurückzugeben.
Außerdem habe ich vergessen, bitte nur .net v2 zu erwähnen
JL.
4
Interessante .NET 2.0-Lösung.
RichardOD
11
Fehlt mir etwas oder funktioniert Ihre .Net 3.5-Lösung nicht? Reverse () kehrt die Liste um und gibt sie nicht zurück. Schade, denn ich hatte auf eine solche Lösung gehofft.
@ user12861, Micky Duncan: Die Antwort ist nicht falsch, Ihnen fehlt etwas. Für System.Collections.Generic.List <T> gibt es eine Reverse-Methode, die eine direkte Umkehrung durchführt. In .Net 3.5 gibt es eine Erweiterungsmethode für IEnumerable <T> mit dem Namen Reverse. Ich habe das Beispiel von var in IEnumerable <int> geändert, um dies deutlicher zu machen.
Matt Howells
55
Wie 280Z28 sagt, können Sie für einen IList<T>einfach den Index verwenden. Sie können dies in einer Erweiterungsmethode ausblenden:
publicstaticIEnumerable<T>FastReverse<T>(thisIList<T> items){for(int i = items.Count-1; i >=0; i--){yieldreturn items[i];}}
Dies ist schneller, als wenn Enumerable.Reverse()zuerst alle Daten gepuffert werden. (Ich glaube nicht, Reversedass Optimierungen auf diese Weise angewendet wurden Count().) Beachten Sie, dass diese Pufferung bedeutet, dass die Daten beim ersten Start der Iteration vollständig gelesen werden, während FastReversealle Änderungen, die während der Iteration an der Liste vorgenommen wurden, "angezeigt" werden. (Es wird auch unterbrochen, wenn Sie mehrere Elemente zwischen den Iterationen entfernen.)
Bei allgemeinen Sequenzen gibt es keine Möglichkeit, umgekehrt zu iterieren - die Sequenz kann unendlich sein, zum Beispiel:
publicstaticIEnumerable<T>GetStringsOfIncreasingSize(){string ret ="";while(true){yieldreturn ret;
ret = ret +"x";}}
Was würden Sie erwarten, wenn Sie versuchen würden, dies in umgekehrter Reihenfolge zu wiederholen?
Nur Neugier, warum "> = 0" anstelle von "> -1" verwenden?
Chris S
15
> warum "> = 0" anstelle von "> -1" verwenden? Weil> = 0 die Absicht besser an Menschen kommuniziert, die den Code lesen. Der Compiler sollte in der Lage sein, dies auf das Äquivalent> -1 zu optimieren, wenn dies die Leistung verbessern würde.
Mark Maslar
1
FastReverse (diese IList <T> Elemente) sollte FastReverse <T> (diese IList <T> Elemente sein. :)
Rob
Das Teilen von Sticks 0 <= i ist noch besser. Nachdem ich im Laufe der Jahre eine ganze Reihe von Kindern Mathematik unterrichtet und Menschen beim Codieren geholfen hatte, stellte ich fest, dass dies die Fehler, die Menschen machen, wenn sie IMMER a> b nach b <a tauschen, erheblich reduziert, da die Dinge dann in der natürlichen Reihenfolge einer Zeile ablaufen von Zahlen (oder der X-Achse eines Koordinatensystems) - der einzige Nachteil ist, wenn Sie eine Gleichung in XML einfügen müssen, sagen wir eine .config
Eske Rahn
13
foreachKehren Sie die Liste vor der Verwendung für die Iteration wie folgt um reverse:
myList.Reverse();foreach(List listItem in myList){Console.WriteLine(listItem);}
Ein Wort der Vorsicht darüber, was myListist, wird hilfreich sein. IEnumerable.Reverse wird hier nicht funktionieren.
Nawfal
2
@ MA-Maddin nein die beiden sind unterschiedlich. Ich denke, der Antwortende verlässt sich auf List <T> .Reverse, die vorhanden ist.
Nawfal
Eigentlich weiß ich nicht, warum ich das gesagt habe. Ich hätte mehr Details hinzufügen sollen ... Auf jeden myListFall ist dies verwirrender Code, da er vom Typ System.Collections.Generic.List<System.Windows.Documents.List>(oder einem anderen benutzerdefinierten ListTyp) zu sein scheint, sonst funktioniert dieser Code nicht: P
Martin Schneider
5
Manchmal haben Sie nicht den Luxus der Indizierung, oder Sie möchten die Ergebnisse einer Linq-Abfrage umkehren oder die Quellensammlung nicht ändern. Wenn eine dieser Angaben zutrifft, kann Linq Ihnen helfen.
Eine Linq-Erweiterungsmethode, die anonyme Typen mit Linq Select verwendet, um einen Sortierschlüssel für Linq OrderByDescending bereitzustellen.
var eable =new[]{"a","b","c"};foreach(var o in eable.Invert()){Console.WriteLine(o);}// "c", "b", "a"
Es heißt "Invertieren", weil es gleichbedeutend mit "Reverse" ist und die Disambiguierung mit der List Reverse-Implementierung ermöglicht.
Es ist auch möglich, bestimmte Bereiche einer Sammlung umzukehren, da Int32.MinValue und Int32.MaxValue außerhalb des Bereichs jeder Art von Sammlungsindex liegen. Wir können sie für den Bestellvorgang nutzen. Wenn ein Elementindex unter dem angegebenen Bereich liegt, wird ihm Int32.MaxValue zugewiesen, damit sich seine Reihenfolge bei Verwendung von OrderByDescending nicht ändert. Ebenso wird Elementen mit einem Index, der größer als der angegebene Bereich ist, Int32.MinValue zugewiesen, damit sie erscheinen am Ende des Bestellvorgangs. Allen Elementen innerhalb des angegebenen Bereichs wird ihr normaler Index zugewiesen und entsprechend umgekehrt.
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);}
Verwendung:
var eable =new[]{"a","b","c","d"};foreach(var o in eable.Invert(1,2)){Console.WriteLine(o);}// "a", "c", "b", "d"
Ich bin mir nicht sicher, welche Leistungseinbußen diese Linq-Implementierungen gegenüber der Verwendung einer temporären Liste zum Umschließen einer Sammlung zum Umkehren haben.
Es ist möglich, wenn Sie den Sammlungscode ändern können, der IEnumerable oder IEnumerable implementiert (z. B. Ihre eigene Implementierung von IList).
Erstellen Sie einen Iterator , der diesen Job für Sie ausführt , z. B. die folgende Implementierung über die IEnumerable- Schnittstelle (vorausgesetzt, 'items' ist in diesem Beispiel ein Listenfeld ):
publicIEnumerator<TObject>GetEnumerator(){for(var i = items.Count-1; i >=0; i--){yieldreturn items[i];}}IEnumeratorIEnumerable.GetEnumerator(){returnGetEnumerator();}
Aus diesem Grund wird Ihre Liste in umgekehrter Reihenfolge durch Ihre Liste iteriert.
Nur ein Hinweis: Sie sollten dieses spezielle Verhalten Ihrer Liste in der Dokumentation klar angeben (noch besser, indem Sie einen selbsterklärenden Klassennamen wie Stack oder Queue wählen).
Dies ist schlecht, da .Reverse()die Liste tatsächlich geändert wird.
Colin Basnett
-8
Das funktioniert ziemlich gut
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++; }
Wie würden Sie das umkehren?Antworten:
Wenn Sie mit einer Liste arbeiten (direkte Indizierung), können Sie dies nicht so effizient tun wie mit einer
for
Schleife.Edit: Was bedeutet in der Regel, wenn Sie sind die Lage , einen verwenden ,
for
Schleife, ist es wahrscheinlich die richtige Methode für diese Aufgabe. Darüber hinausforeach
ist das Konstrukt selbst so weit wie in der Reihenfolge implementiert implementiert, um Schleifen auszudrücken, die unabhängig von Elementindizes und Iterationsreihenfolge sind, was bei der parallelen Programmierung besonders wichtig ist . Es ist meiner Meinung nach, dass Iteration auf Bestellung verlassen sollte nicht verwendet werdenforeach
für Looping.quelle
yield return
" möglich ist .Wenn Sie mit .NET 3.5 arbeiten, können Sie Folgendes tun:
Es ist nicht sehr effizient, da es im Grunde genommen den Enumerator durchlaufen muss, um alles auf einen Stapel zu legen, und dann alles in umgekehrter Reihenfolge wieder herausspringt.
Wenn Sie eine direkt indizierbare Sammlung haben (z. B. IList), sollten Sie
for
stattdessen auf jeden Fall eine Schleife verwenden.Wenn Sie sich in .NET 2.0 befinden und keine for-Schleife verwenden können (dh Sie haben nur eine IEnumerable), müssen Sie nur Ihre eigene Umkehrfunktion schreiben. Das sollte funktionieren:
Dies beruht auf einem Verhalten, das vielleicht nicht so offensichtlich ist. Wenn Sie eine IEnumerable an den Stapelkonstruktor übergeben, durchläuft sie diese und schiebt die Elemente auf den Stapel. Wenn Sie dann durch den Stapel iterieren, werden die Dinge in umgekehrter Reihenfolge wieder herausgeschleudert.
Diese und die .NET 3.5-
Reverse()
Erweiterungsmethode werden offensichtlich in die Luft gesprengt, wenn Sie eine IEnumerable eingeben, die niemals aufhört, Elemente zurückzugeben.quelle
Wie 280Z28 sagt, können Sie für einen
IList<T>
einfach den Index verwenden. Sie können dies in einer Erweiterungsmethode ausblenden:Dies ist schneller, als wenn
Enumerable.Reverse()
zuerst alle Daten gepuffert werden. (Ich glaube nicht,Reverse
dass Optimierungen auf diese Weise angewendet wurdenCount()
.) Beachten Sie, dass diese Pufferung bedeutet, dass die Daten beim ersten Start der Iteration vollständig gelesen werden, währendFastReverse
alle Änderungen, die während der Iteration an der Liste vorgenommen wurden, "angezeigt" werden. (Es wird auch unterbrochen, wenn Sie mehrere Elemente zwischen den Iterationen entfernen.)Bei allgemeinen Sequenzen gibt es keine Möglichkeit, umgekehrt zu iterieren - die Sequenz kann unendlich sein, zum Beispiel:
Was würden Sie erwarten, wenn Sie versuchen würden, dies in umgekehrter Reihenfolge zu wiederholen?
quelle
foreach
Kehren Sie die Liste vor der Verwendung für die Iteration wie folgt umreverse
:quelle
myList
ist, wird hilfreich sein. IEnumerable.Reverse wird hier nicht funktionieren.myList
Fall ist dies verwirrender Code, da er vom TypSystem.Collections.Generic.List<System.Windows.Documents.List>
(oder einem anderen benutzerdefiniertenList
Typ) zu sein scheint, sonst funktioniert dieser Code nicht: PManchmal haben Sie nicht den Luxus der Indizierung, oder Sie möchten die Ergebnisse einer Linq-Abfrage umkehren oder die Quellensammlung nicht ändern. Wenn eine dieser Angaben zutrifft, kann Linq Ihnen helfen.
Eine Linq-Erweiterungsmethode, die anonyme Typen mit Linq Select verwendet, um einen Sortierschlüssel für Linq OrderByDescending bereitzustellen.
Verwendung:
Es heißt "Invertieren", weil es gleichbedeutend mit "Reverse" ist und die Disambiguierung mit der List Reverse-Implementierung ermöglicht.
Es ist auch möglich, bestimmte Bereiche einer Sammlung umzukehren, da Int32.MinValue und Int32.MaxValue außerhalb des Bereichs jeder Art von Sammlungsindex liegen. Wir können sie für den Bestellvorgang nutzen. Wenn ein Elementindex unter dem angegebenen Bereich liegt, wird ihm Int32.MaxValue zugewiesen, damit sich seine Reihenfolge bei Verwendung von OrderByDescending nicht ändert. Ebenso wird Elementen mit einem Index, der größer als der angegebene Bereich ist, Int32.MinValue zugewiesen, damit sie erscheinen am Ende des Bestellvorgangs. Allen Elementen innerhalb des angegebenen Bereichs wird ihr normaler Index zugewiesen und entsprechend umgekehrt.
Verwendung:
Ich bin mir nicht sicher, welche Leistungseinbußen diese Linq-Implementierungen gegenüber der Verwendung einer temporären Liste zum Umschließen einer Sammlung zum Umkehren haben.
Zum Zeitpunkt des Schreibens war mir die Reverse-Implementierung von Linq nicht bekannt, aber es hat Spaß gemacht, dies herauszufinden. https://msdn.microsoft.com/en-us/library/vstudio/bb358497(v=vs.100).aspx
quelle
Wenn Sie eine Liste <T> verwenden, können Sie auch diesen Code verwenden:
Dies ist eine Methode, die die Liste in sich selbst umgekehrt schreibt.
Nun zum foreach:
Die Ausgabe ist:
quelle
Es ist möglich, wenn Sie den Sammlungscode ändern können, der IEnumerable oder IEnumerable implementiert (z. B. Ihre eigene Implementierung von IList).
Erstellen Sie einen Iterator , der diesen Job für Sie ausführt , z. B. die folgende Implementierung über die IEnumerable- Schnittstelle (vorausgesetzt, 'items' ist in diesem Beispiel ein Listenfeld ):
Aus diesem Grund wird Ihre Liste in umgekehrter Reihenfolge durch Ihre Liste iteriert.
Nur ein Hinweis: Sie sollten dieses spezielle Verhalten Ihrer Liste in der Dokumentation klar angeben (noch besser, indem Sie einen selbsterklärenden Klassennamen wie Stack oder Queue wählen).
quelle
Nein. ForEach durchläuft nur die Sammlung für jeden Artikel und die Reihenfolge hängt davon ab, ob IEnumerable oder GetEnumerator () verwendet wird.
quelle
Wenn man die nette Antwort von Jon Skeet leicht herausarbeitet , könnte dies vielseitig sein:
Und dann verwenden als
quelle
Ich habe diesen Code verwendet, der funktioniert hat
quelle
.Reverse()
die Liste tatsächlich geändert wird.Das funktioniert ziemlich gut
quelle