Ich muss mich rückwärts durch ein Array bewegen, damit ich folgenden Code habe:
for (int i = myArray.Length - 1; i >= 0; i--)
{
// Do something
myArray[i] = 42;
}
Gibt es einen besseren Weg, dies zu tun?
Update: Ich hatte gehofft, dass C # vielleicht einen eingebauten Mechanismus dafür hat, wie:
foreachbackwards (int i in myArray)
{
// so easy
}
Update 2: Es gibt bessere Möglichkeiten. Rune erhält den Preis mit:
for (int i = myArray.Length; i-- > 0; )
{
//do something
}
//or
for (int i = myArray.Length; i --> 0; )
{
// do something
}
was in normalem C noch besser aussieht (dank Twotymz):
for (int i = lengthOfArray; i--; )
{
//do something
}
Antworten:
Obwohl dies zugegebenermaßen etwas dunkel ist, würde ich sagen, dass der typografisch angenehmste Weg, dies zu tun, der ist
quelle
In C ++ haben Sie grundsätzlich die Wahl zwischen Iteration mit Iteratoren oder Indizes. Abhängig davon, ob Sie ein einfaches Array oder ein
std::vector
, verwenden Sie unterschiedliche Techniken.Verwenden von std :: vector
Verwenden von Iteratoren
Mit C ++ können Sie dies mit tun
std::reverse_iterator:
Indizes verwenden
Der vorzeichenlose Integraltyp, der von zurückgegeben wird,
std::vector<T>::size
ist nicht immerstd::size_t
. Es kann größer oder kleiner sein. Dies ist entscheidend, damit die Schleife funktioniert.Es funktioniert, da vorzeichenlose Integraltypwerte mittels Modulo ihrer Bitanzahl definiert werden. Also, wenn Sie einstellen
-N
, landen Sie bei(2 ^ BIT_SIZE) -N
Arrays verwenden
Verwenden von Iteratoren
Wir verwenden
std::reverse_iterator
die Iteration.Indizes verwenden
Wir können hier sicher verwenden
std::size_t
, im Gegensatz zu oben, dasizeof
immerstd::size_t
per Definition zurückgegeben wird.Vermeiden von Fallstricken, bei denen sizeof auf Zeiger angewendet wird
Tatsächlich ist die obige Methode zum Bestimmen der Größe eines Arrays zum Kotzen. Wenn a tatsächlich ein Zeiger anstelle eines Arrays ist (was ziemlich häufig vorkommt und Anfänger es verwirren), schlägt es stillschweigend fehl. Eine bessere Möglichkeit besteht darin, Folgendes zu verwenden, das beim Kompilieren fehlschlägt, wenn ein Zeiger angegeben wird:
Es funktioniert, indem zuerst die Größe des übergebenen Arrays ermittelt und dann deklariert wird, dass ein Verweis auf ein Array vom Typ char derselben Größe zurückgegeben wird.
char
ist definiert alssizeof
: 1. Das zurückgegebene Array hat also asizeof
: N * 1, was wir suchen, mit nur Auswertung der Kompilierungszeit und null Laufzeit-Overhead.Anstatt zu tun
Ändern Sie Ihren Code so, dass er jetzt funktioniert
quelle
end
undbegin
in umgekehrter Reihenfolge?Geben Sie in C # mit Visual Studio 2005 oder höher 'forr' ein und drücken Sie [TAB] [TAB] . Dies wird zu einer
for
Schleife erweitert, die eine Sammlung rückwärts durchläuft.Es ist so leicht, sich zu irren (zumindest für mich), dass ich dachte, dieses Snippet einzulegen wäre eine gute Idee.
Das heißt, ich mag
Array.Reverse()
/Enumerable.Reverse()
und iteriere dann besser vorwärts - sie geben die Absicht klarer an.quelle
Ich würde immer klaren Code dem typografisch ansprechenden Code vorziehen . Daher würde ich immer verwenden:
Sie können dies als Standardmethode für die Rückwärtsschleife betrachten.
Nur meine zwei Cent ...
quelle
In C # mit Linq :
quelle
Dies ist definitiv der beste Weg für jedes Array, dessen Länge ein vorzeichenbehafteter integraler Typ ist. Bei Arrays, deren Länge ein vorzeichenloser Integraltyp ist (z. B.
std::vector
in C ++), müssen Sie die Endbedingung geringfügig ändern:Wenn Sie gerade gesagt haben
i >= 0
, gilt dies immer für eine Ganzzahl ohne Vorzeichen, sodass die Schleife eine Endlosschleife ist.quelle
Sieht gut für mich aus. Wenn der Indexer nicht signiert war (uint usw.), müssen Sie dies möglicherweise berücksichtigen. Nennen Sie mich faul, aber in diesem (nicht signierten) Fall könnte ich nur eine Gegenvariable verwenden:
(Eigentlich müssten Sie auch hier auf Fälle wie arr.Length = uint.MaxValue achten ... vielleicht a! = irgendwo ... natürlich ist das ein sehr unwahrscheinlicher Fall!)
quelle
In CI machen Sie das gerne:
C # -Beispiel von MusiGenesis hinzugefügt:
quelle
Der beste Weg, dies in C ++ zu tun, ist wahrscheinlich die Verwendung von Iteratoradaptern (oder besser von Bereichsadaptern), die die Sequenz beim Durchlaufen träge transformieren.
Grundsätzlich,
Zeigt den Bereich "Bereich" (hier ist er leer, aber ich bin mir ziemlich sicher, dass Sie Elemente selbst hinzufügen können) in umgekehrter Reihenfolge an. Natürlich ist es nicht sehr sinnvoll, den Bereich einfach zu iterieren, aber es ist ziemlich cool, diesen neuen Bereich an Algorithmen und andere Dinge weiterzugeben.
Dieser Mechanismus kann auch für viel leistungsfähigere Zwecke verwendet werden:
Berechnet den Bereich "Bereich" träge, wobei die Funktion "f" auf alle Elemente angewendet wird, Elemente, für die "p" nicht wahr ist, entfernt werden und schließlich der resultierende Bereich umgekehrt wird.
Die Pipe-Syntax ist aufgrund ihres Infix die am besten lesbare IMO. Das ausstehende Boost.Range-Bibliotheksupdate implementiert dies, aber es ist ziemlich einfach, es auch selbst zu tun. Mit einem Lambda-DSEL ist es noch cooler, die Funktion f und das Prädikat p inline zu generieren.
quelle
quelle
Ich bevorzuge eine while-Schleife. Für mich ist es klarer, als
i
den Zustand einer for-Schleife zu dekrementierenquelle
Ich würde den Code in der ursprünglichen Frage verwenden, aber wenn Sie wirklich foreach verwenden möchten und einen ganzzahligen Index in C # haben möchten:
quelle
Ich werde versuchen, meine eigene Frage hier zu beantworten, aber das gefällt mir auch nicht wirklich:
quelle
HINWEIS: Dieser Beitrag war viel detaillierter und daher nicht thematisch. Ich entschuldige mich.
Davon abgesehen lesen meine Kollegen es und glauben, dass es "irgendwo" wertvoll ist. Dieser Thread ist nicht der richtige Ort. Ich würde mich über Ihr Feedback freuen, wohin dies führen soll (ich bin neu auf der Website).
Auf jeden Fall ist dies die C # -Version in .NET 3.5, die insofern erstaunlich ist, als sie mit der definierten Semantik für jeden Sammlungstyp funktioniert. Dies ist eine Standardmaßnahme (Wiederverwendung!), Nicht die Leistung oder die Minimierung des CPU-Zyklus im gängigsten Entwicklungsszenario, obwohl dies in der realen Welt niemals der Fall zu sein scheint (vorzeitige Optimierung).
*** Erweiterungsmethode, die über einen beliebigen Sammlungstyp arbeitet und einen Aktionsdelegierten ausführt, der einen einzelnen Wert des Typs erwartet, die alle in umgekehrter Reihenfolge über jedes Element ausgeführt werden **
Anforderungen 3.5:
Ältere .NET-Versionen oder möchten Sie Linq-Interna besser verstehen? Lesen Sie weiter .. Oder nicht ..
ANNAHME: Im .NET-Typsystem erbt der Array-Typ von der IEnumerable-Schnittstelle (nicht von der generischen IEnumerable, sondern nur von IEnumerable).
Dies ist alles, was Sie brauchen, um von Anfang bis Ende zu iterieren, aber Sie möchten sich in die entgegengesetzte Richtung bewegen. Da IEnumerable mit einem Array vom Typ 'Objekt' funktioniert, ist jeder Typ gültig.
KRITISCHE MASSNAHME: Wir gehen davon aus, dass Sie eine Sequenz in umgekehrter Reihenfolge verarbeiten können, die "besser" ist, als dies nur für ganze Zahlen möglich ist.
Lösung a für .NET CLR 2.0-3.0:
Beschreibung: Wir akzeptieren jede IEnumerable-Implementierungsinstanz mit dem Mandat, dass jede darin enthaltene Instanz vom gleichen Typ ist. Wenn wir also ein Array erhalten, enthält das gesamte Array Instanzen vom Typ X. Wenn andere Instanzen vom Typ! = X sind, wird eine Ausnahme ausgelöst:
Ein Singleton-Service:
öffentliche Klasse ReverserService {private ReverserService () {}
[TestFixture] öffentliche Klasse Testing123 {
quelle