So che potrei usare for
un'istruzione e ottenere lo stesso effetto, ma posso fare un ciclo indietro in un foreach
ciclo in C #?
IEnumerable<int> Infinity() { int i = 1; while (true) yield return i++; }
come la invertiresti?
So che potrei usare for
un'istruzione e ottenere lo stesso effetto, ma posso fare un ciclo indietro in un foreach
ciclo in C #?
IEnumerable<int> Infinity() { int i = 1; while (true) yield return i++; }
come la invertiresti?
Risposte:
Quando si lavora con un elenco (indicizzazione diretta), non è possibile farlo in modo efficiente come l'utilizzo di un for
ciclo.
Modifica: in genere significa che, quando si è in grado di utilizzare un for
ciclo, è probabilmente il metodo corretto per questa attività. Inoltre, per quanto foreach
implementato in ordine, il costrutto stesso è costruito per esprimere loop indipendenti dagli indici degli elementi e dall'ordine di iterazione, che è particolarmente importante nella programmazione parallela . E ' mia opinione che l'iterazione basandosi su ordine non dovrebbe usare foreach
per loop.
yield return
".
Se sei su .NET 3.5 puoi farlo:
IEnumerable<int> enumerableThing = ...;
foreach (var x in enumerableThing.Reverse())
Non è molto efficiente in quanto deve sostanzialmente passare attraverso l'enumeratore in avanti mettendo tutto in pila, quindi rimuove tutto in ordine inverso.
Se disponi di una raccolta indicizzabile direttamente (ad esempio IList), devi for
invece utilizzare un ciclo.
Se sei su .NET 2.0 e non puoi usare un ciclo for (cioè hai solo un IEnumerable) allora dovrai solo scrivere la tua funzione Reverse. Questo dovrebbe funzionare:
static IEnumerable<T> Reverse<T>(IEnumerable<T> input)
{
return new Stack<T>(input);
}
Ciò si basa su un comportamento che forse non è così ovvio. Quando si passa un IEnumerable al costruttore dello stack, scorrerà attraverso di esso e spingerà gli elementi nello stack. Quando si scorre attraverso la pila, le cose vengono rimosse in ordine inverso.
Questo e il Reverse()
metodo di estensione .NET 3.5 ovviamente esploderanno se lo alimenterai con un IEnumerable che non smette mai di restituire articoli.
Come dice 280Z28, per un IList<T>
puoi semplicemente usare l'indice. Puoi nasconderlo in un metodo di estensione:
public static IEnumerable<T> FastReverse<T>(this IList<T> items)
{
for (int i = items.Count-1; i >= 0; i--)
{
yield return items[i];
}
}
Questo sarà più veloce di quello Enumerable.Reverse()
che buffer prima tutti i dati. (Non credo che Reverse
siano state applicate ottimizzazioni nel modo in cui lo Count()
fa.) Si noti che questo buffering significa che i dati vengono letti completamente quando si inizia a iterare per la prima volta, mentre FastReverse
"vedranno" eventuali modifiche apportate all'elenco mentre si esegue l'iterazione. (Si romperà anche se rimuovi più elementi tra le iterazioni.)
Per le sequenze generali, non c'è modo di iterare al contrario: la sequenza potrebbe essere infinita, ad esempio:
public static IEnumerable<T> GetStringsOfIncreasingSize()
{
string ret = "";
while (true)
{
yield return ret;
ret = ret + "x";
}
}
Cosa ti aspetteresti che accadesse se provassi a scorrere su quello al contrario?
Prima di utilizzare foreach
per l'iterazione, invertire l'elenco con il reverse
metodo:
myList.Reverse();
foreach( List listItem in myList)
{
Console.WriteLine(listItem);
}
myList
è sarà utile. IEnumerable.Reverse non funzionerà qui.
myList
sembra essere di tipo System.Collections.Generic.List<System.Windows.Documents.List>
(o qualsiasi altro List
tipo personalizzato ) altrimenti questo codice non funziona: P
A volte non hai il lusso di indicizzare, o forse vuoi invertire i risultati di una query Linq, o forse non vuoi modificare la raccolta dei sorgenti, se qualcuno di questi è vero, Linq può aiutarti.
Un metodo di estensione Linq che utilizza tipi anonimi con Linq Select per fornire una chiave di ordinamento per Linq OrderByDescending;
public static IEnumerable<T> Invert<T>(this IEnumerable<T> source)
{
var transform = source.Select(
(o, i) => new
{
Index = i,
Object = o
});
return transform.OrderByDescending(o => o.Index)
.Select(o => o.Object);
}
Uso:
var eable = new[]{ "a", "b", "c" };
foreach(var o in eable.Invert())
{
Console.WriteLine(o);
}
// "c", "b", "a"
Si chiama "Invert" perché è sinonimo di "Reverse" e consente di chiarire le ambizioni con l'implementazione di List Reverse.
È possibile invertire anche determinati intervalli di una raccolta, poiché Int32.MinValue e Int32.MaxValue sono al di fuori dell'intervallo di qualsiasi tipo di indice di raccolta, possiamo sfruttarli per il processo di ordinazione; se un indice di elemento è al di sotto dell'intervallo dato, viene assegnato Int32.MaxValue in modo che il suo ordine non cambi quando si utilizza OrderByDescending, allo stesso modo, agli elementi in un indice maggiore dell'intervallo dato, verrà assegnato Int32.MinValue, in modo che sembra alla fine del processo di ordinazione. A tutti gli elementi all'interno dell'intervallo specificato viene assegnato il loro indice normale e vengono invertiti di conseguenza.
public static IEnumerable<T> Invert<T>(this IEnumerable<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);
}
Uso:
var eable = new[]{ "a", "b", "c", "d" };
foreach(var o in eable.Invert(1, 2))
{
Console.WriteLine(o);
}
// "a", "c", "b", "d"
Non sono sicuro dei risultati delle prestazioni di queste implementazioni Linq rispetto all'uso di un elenco temporaneo per racchiudere una raccolta per l'inversione.
Al momento della stesura, non ero a conoscenza dell'implementazione inversa di Linq, tuttavia, è stato divertente farlo. https://msdn.microsoft.com/en-us/library/vstudio/bb358497(v=vs.100).aspx
Se usi un Elenco <T>, puoi anche usare questo codice:
List<string> list = new List<string>();
list.Add("1");
list.Add("2");
list.Add("3");
list.Reverse();
Questo è un metodo che scrive la lista al contrario in sé.
Ora la foreach:
foreach(string s in list)
{
Console.WriteLine(s);
}
L'output è:
3
2
1
È possibile se è possibile modificare il codice di raccolta che implementa IEnumerable o IEnumerable (ad es. La propria implementazione di IList).
Crea un Iteratore che fa questo lavoro per te, ad esempio come la seguente implementazione attraverso l' interfaccia IEnumerable (supponendo che 'items' sia un campo Elenco in questo esempio):
public IEnumerator<TObject> GetEnumerator()
{
for (var i = items.Count - 1; i >= 0; i--)
{
yield return items[i];
}
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
Per questo motivo, l'elenco verrà ripetuto in ordine inverso nell'elenco.
Solo un suggerimento: dovresti dichiarare chiaramente questo comportamento speciale della tua lista all'interno della documentazione (ancora meglio scegliendo un nome di classe che si spiega da solo come Stack o Queue).
No. ForEach esegue l'iterazione della raccolta per ciascun elemento e l'ordine dipende dal fatto che utilizzi IEnumerable o GetEnumerator ().
Elaborando leggermente la bella risposta di Jon Skeet , questo potrebbe essere versatile:
public static IEnumerable<T> Directional<T>(this IList<T> items, bool Forwards) {
if (Forwards) foreach (T item in items) yield return item;
else for (int i = items.Count-1; 0<=i; i--) yield return items[i];
}
E poi usa come
foreach (var item in myList.Directional(forwardsCondition)) {
.
.
}
Ho usato questo codice che ha funzionato
if (element.HasAttributes) {
foreach(var attr in element.Attributes().Reverse())
{
if (depth > 1)
{
elements_upper_hierarchy_text = "";
foreach (var ancest in element.Ancestors().Reverse())
{
elements_upper_hierarchy_text += ancest.Name + "_";
}// foreach(var ancest in element.Ancestors())
}//if (depth > 1)
xml_taglist_report += " " + depth + " " + elements_upper_hierarchy_text+ element.Name + "_" + attr.Name +"(" + attr.Name +")" + " = " + attr.Value + "\r\n";
}// foreach(var attr in element.Attributes().Reverse())
}// if (element.HasAttributes) {
.Reverse()
sta effettivamente modificando l'elenco.
Funziona abbastanza bene
List<string> list = new List<string>();
list.Add("Hello");
list.Add("Who");
list.Add("Are");
list.Add("You");
foreach (String s in list)
{
Console.WriteLine(list[list.Count - list.IndexOf(s) - 1]);
}