Wiem, że mogę użyć for
instrukcji i osiągnąć ten sam efekt, ale czy mogę wykonać pętlę wstecz przez foreach
pętlę w języku C #?
IEnumerable<int> Infinity() { int i = 1; while (true) yield return i++; }
Jak byś to odwrócił?
Wiem, że mogę użyć for
instrukcji i osiągnąć ten sam efekt, ale czy mogę wykonać pętlę wstecz przez foreach
pętlę w języku C #?
IEnumerable<int> Infinity() { int i = 1; while (true) yield return i++; }
Jak byś to odwrócił?
Odpowiedzi:
Podczas pracy z listą (indeksowanie bezpośrednie) nie można tego robić tak wydajnie, jak używanie for
pętli.
Edit: co generalnie oznacza, gdy jesteś w stanie użyć for
pętli, prawdopodobnie poprawna metoda dla tego zadania. Dodatkowo, o ile foreach
jest implementowana w kolejności, sama konstrukcja jest zbudowana do wyrażania pętli niezależnych od indeksów elementów i kolejności iteracji, co jest szczególnie ważne w programowaniu równoległym . To jest moja opinia , że iteracja polegając na celu nie należy używać foreach
do pętli.
yield return
”.
Jeśli korzystasz z .NET 3.5, możesz to zrobić:
IEnumerable<int> enumerableThing = ...;
foreach (var x in enumerableThing.Reverse())
Nie jest to zbyt wydajne, ponieważ w zasadzie musi przejść przez moduł wyliczający do przodu, umieszczając wszystko na stosie, a następnie wyrzucając wszystko z powrotem w odwrotnej kolejności.
Jeśli masz kolekcję, którą można bezpośrednio indeksować (np. IList), zdecydowanie powinieneś for
zamiast tego użyć pętli.
Jeśli korzystasz z .NET 2.0 i nie możesz użyć pętli for (np. Masz tylko IEnumerable), będziesz musiał po prostu napisać własną funkcję Reverse. To powinno działać:
static IEnumerable<T> Reverse<T>(IEnumerable<T> input)
{
return new Stack<T>(input);
}
Zależy to od pewnego zachowania, które być może nie jest takie oczywiste. Po przekazaniu elementu IEnumerable do konstruktora stosu, będzie on iterował przez niego i wypycha elementy na stos. Kiedy następnie przechodzisz przez stos, wyrzuca elementy z powrotem w odwrotnej kolejności.
Ta i Reverse()
metoda rozszerzenia .NET 3.5 oczywiście wybuchną, jeśli podasz mu IEnumerable, który nigdy nie przestanie zwracać elementów.
Jak mówi 280Z28, IList<T>
możesz po prostu użyć indeksu. Możesz to ukryć w metodzie rozszerzenia:
public static IEnumerable<T> FastReverse<T>(this IList<T> items)
{
for (int i = items.Count-1; i >= 0; i--)
{
yield return items[i];
}
}
Będzie to szybsze niż pierwsze Enumerable.Reverse()
buforowanie wszystkich danych. (Nie sądzę Reverse
, aby zastosowano jakieś optymalizacje w taki sposób Count()
). Zauważ, że to buforowanie oznacza, że dane są odczytywane w całości przy pierwszym uruchomieniu iteracji, podczas FastReverse
gdy podczas iteracji "zobaczy" wszelkie zmiany dokonane na liście. (Zepsuje się również, jeśli usuniesz wiele elementów między iteracjami).
W przypadku sekwencji ogólnych nie ma możliwości wykonywania iteracji w odwrotnej kolejności - sekwencja może być nieskończona, na przykład:
public static IEnumerable<T> GetStringsOfIncreasingSize()
{
string ret = "";
while (true)
{
yield return ret;
ret = ret + "x";
}
}
Czego byś się spodziewał, gdybyś próbował powtórzyć to w odwrotnej kolejności?
Przed użyciem foreach
do iteracji odwróć listę za pomocą reverse
metody:
myList.Reverse();
foreach( List listItem in myList)
{
Console.WriteLine(listItem);
}
myList
jest pomocne. IEnumerable.Reverse nie będzie tutaj działać.
myList
wydaje się być typu System.Collections.Generic.List<System.Windows.Documents.List>
(lub innego List
typu niestandardowego ), w przeciwnym razie ten kod nie działa: P
Czasami nie masz luksusu indeksowania, a może chcesz cofnąć wyniki zapytania Linq, a może nie chcesz modyfikować kolekcji źródłowej, jeśli którekolwiek z nich jest prawdą, Linq może ci pomóc.
Metoda rozszerzenia Linq używająca anonimowych typów z Linq Select w celu zapewnienia klucza sortowania dla 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);
}
Stosowanie:
var eable = new[]{ "a", "b", "c" };
foreach(var o in eable.Invert())
{
Console.WriteLine(o);
}
// "c", "b", "a"
Nazywa się „Invert”, ponieważ jest synonimem „Reverse” i umożliwia ujednoznacznienie z implementacją List Reverse.
Możliwe jest również odwrócenie pewnych zakresów kolekcji, ponieważ Int32.MinValue i Int32.MaxValue są poza zakresem jakiegokolwiek indeksu kolekcji, możemy je wykorzystać do procesu zamawiania; jeśli indeks elementu jest poniżej podanego zakresu, jest przypisywany Int32.MaxValue, aby jego kolejność nie zmieniała się podczas korzystania z OrderByDescending, podobnie elementy o indeksie większym niż podany zakres zostaną przypisane Int32.MinValue, więc pojawiają się na końcu procesu składania zamówienia. Wszystkim elementom w podanym zakresie przypisuje się normalny indeks i odpowiednio je odwraca.
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);
}
Stosowanie:
var eable = new[]{ "a", "b", "c", "d" };
foreach(var o in eable.Invert(1, 2))
{
Console.WriteLine(o);
}
// "a", "c", "b", "d"
Nie jestem pewien wydajności tych implementacji Linq w porównaniu z użyciem tymczasowej listy do pakowania kolekcji do cofania.
W momencie pisania nie byłem świadomy własnej implementacji Reverse firmy Linq, ale praca nad tym była zabawna. https://msdn.microsoft.com/en-us/library/vstudio/bb358497(v=vs.100).aspx
Jeśli używasz List <T>, możesz również użyć tego kodu:
List<string> list = new List<string>();
list.Add("1");
list.Add("2");
list.Add("3");
list.Reverse();
Jest to metoda, która sama w sobie zapisuje listę w odwrotnej kolejności.
Teraz foreach:
foreach(string s in list)
{
Console.WriteLine(s);
}
Wynik to:
3
2
1
Jest to możliwe, jeśli możesz zmienić kod kolekcji, który implementuje IEnumerable lub IEnumerable (np. Własną implementację IList).
Utwórz Iterator wykonujący to zadanie za Ciebie, na przykład jak następująca implementacja za pośrednictwem interfejsu IEnumerable (zakładając, że „items” to pole listy w tym przykładzie):
public IEnumerator<TObject> GetEnumerator()
{
for (var i = items.Count - 1; i >= 0; i--)
{
yield return items[i];
}
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
Z tego powodu Twoja lista będzie iterować w odwrotnej kolejności na liście.
Wskazówka: powinieneś jasno określić to szczególne zachowanie swojej listy w dokumentacji (jeszcze lepiej, wybierając samoobjaśniającą się nazwę klasy, taką jak Stack lub Queue).
Nie. ForEach po prostu wykonuje iterację kolekcji dla każdego elementu, a zamówienie zależy od tego, czy używa IEnumerable, czy GetEnumerator ().
Opierając się słabo na ładnej odpowiedzi Jona Skeeta , może to być wszechstronne:
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];
}
A następnie użyj jako
foreach (var item in myList.Directional(forwardsCondition)) {
.
.
}
Użyłem tego kodu, który zadziałał
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()
faktycznie modyfikuje listę.
Działa to całkiem nieźle
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]);
}