Musiałbyś utworzyć wystąpienie wszystkich elementów w wyliczeniu, abyś mógł odwrócić ich kolejność. To może nie być możliwe. Rozważ sekwencję: IEnumerable<int> Infinity() { int i = 1; while (true) yield return i++; }Jak byś to odwrócił?
Suncat2000
Odpowiedzi:
84
Podczas pracy z listą (indeksowanie bezpośrednie) nie można tego robić tak wydajnie, jak używanie forpętli.
Edit: co generalnie oznacza, gdy jesteś w stanie użyć forpętli, prawdopodobnie poprawna metoda dla tego zadania. Dodatkowo, o ile foreachjest 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ć foreachdo pętli.
Twoje ostatnie stwierdzenie wydaje mi się zbyt ogólne. Z pewnością są przypadki, w których na przykład jakiś rodzaj IEnumerable musi być iterowany po kolei? Czy w takim przypadku nie użyłbyś foreach? Czego byś użył?
avl_sweden
1
AKTUALIZACJA: Zobacz odpowiedź [Bryana na podobne pytanie [( stackoverflow.com/a/3320924/199364 ), aby uzyskać bardziej nowoczesną odpowiedź, używając Linq i omawiając obie listy i inne wyliczalne.
ToolmakerSteve
AKTUALIZACJA: Jon Skeet ma jeszcze bardziej elegancką odpowiedź, która jest teraz możliwa przy użyciu „ yield return”.
ToolmakerSteve
2
Miałem taką samą początkową reakcję jak @avl_sweden - „iteracja oparta na kolejności nie powinna używać foreach” brzmi zbyt szeroko. Tak, foreach jest dobre do wyrażania niezależnych od kolejności zadań równoległych. ALE jest to również istotna część nowoczesnego programowania opartego na iteratorach . Być może chodzi o to, że byłoby jaśniej / bezpieczniej, gdyby istniały dwa odrębne słowa kluczowe , aby wyjaśnić, czy jedno z nich twierdzi, że iteracje są niezależne od kolejności? [Biorąc pod uwagę język taki jak Eiffel, który może propagować kontrakty, takie twierdzenia mogą być prawdziwe lub fałszywe dla danego kodu.]
ToolmakerSteve
2
Pomysł, że foreach „jest zbudowany do wyrażania pętli niezależnych od indeksów elementów i kolejności iteracji” jest niepoprawny. Specyfikacja języka C # wymaga uporządkowania wszystkich elementów procesu. Albo za pomocą MoveNext na iteratorach, albo przez przetwarzanie indeksów zaczynających się od zera i zwiększanych o jeden przy każdej iteracji tablic.
Donald Rich,
145
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ś forzamiast 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ć:
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.
Czy coś mi brakuje lub czy Twoje rozwiązanie .Net 3.5 w rzeczywistości nie działa? Reverse () odwraca listę w miejscu i nie zwraca jej. Szkoda, bo liczyłem na takie rozwiązanie.
@ user12861, Micky Duncan: odpowiedź nie jest zła, czegoś brakuje. W System.Collections.Generic.List <T> istnieje metoda Reverse, która wykonuje odwrócenie w miejscu. W .Net 3.5 istnieje metoda rozszerzająca IEnumerable <T> o nazwie Reverse. Zmieniłem przykład z var na IEnumerable <int>, aby było to bardziej wyraźne.
Matt Howells
55
Jak mówi 280Z28, IList<T>możesz po prostu użyć indeksu. Możesz to ukryć w metodzie rozszerzenia:
publicstaticIEnumerable<T>FastReverse<T>(thisIList<T> items){for(int i = items.Count-1; i >=0; i--){yieldreturn 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 FastReversegdy 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:
publicstaticIEnumerable<T>GetStringsOfIncreasingSize(){string ret ="";while(true){yieldreturn ret;
ret = ret +"x";}}
Czego byś się spodziewał, gdybyś próbował powtórzyć to w odwrotnej kolejności?
Tylko ciekawość, po co używać „> = 0” zamiast „> -1”?
Chris S,
15
> po co używać „> = 0” zamiast „> -1”? Ponieważ> = 0 lepiej przekazuje intencję ludziom czytającym kod. Kompilator powinien być w stanie zoptymalizować to do odpowiednika> -1, jeśli poprawiłoby to wydajność.
Mark Maslar
1
FastReverse (te elementy IList <T>) powinny być FastReverse <T> (te elementy IList <T>. :)
Rob
Rozłupywanie prętów 0 <= i jest jeszcze lepsze. Po nauczeniu przez lata wielu dzieci matematyki i pomocy ludziom w programowaniu, odkryłem, że znacznie zmniejsza to liczbę błędów popełnianych przez ludzi, jeśli ZAWSZE zamieniają a> b na b <a, ponieważ rzeczy przybierają wtedy naturalną kolejność linii liczb (Lub oś X układu współrzędnych) - jedyną wadą jest wklejenie równania do XML, powiedzmy a .config
Eske Rahn
13
Przed użyciem foreachdo iteracji odwróć listę za pomocą reversemetody:
myList.Reverse();foreach(List listItem in myList){Console.WriteLine(listItem);}
Słowo ostrzeżenia na temat tego, co myListjest pomocne. IEnumerable.Reverse nie będzie tutaj działać.
nawfal
2
@ MA-Maddin nie, te dwa są różne. Domyślam się, że osoba odpowiadająca polega na liście <T> .Reverse, która jest na miejscu.
nawfal
Właściwie nie wiem, dlaczego to powiedziałem. Powinienem był dodać więcej szczegółów ... W każdym razie jest to mylący kod, ponieważ myListwydaje się być typu System.Collections.Generic.List<System.Windows.Documents.List>(lub innego Listtypu niestandardowego ), w przeciwnym razie ten kod nie działa: P
Martin Schneider
5
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;
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.
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);}
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.
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):
publicIEnumerator<TObject>GetEnumerator(){for(var i = items.Count-1; i >=0; i--){yieldreturn items[i];}}IEnumeratorIEnumerable.GetEnumerator(){returnGetEnumerator();}
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).
To jest złe, ponieważ .Reverse()faktycznie modyfikuje listę.
Colin Basnett,
-8
Działa to całkiem nieźle
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++; }
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 ileforeach
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.źródło
yield return
”.Jeśli korzystasz z .NET 3.5, możesz to zrobić:
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ć:
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.źródło
Jak mówi 280Z28,
IList<T>
możesz po prostu użyć indeksu. Możesz to ukryć w metodzie rozszerzenia:Będzie to szybsze niż pierwsze
Enumerable.Reverse()
buforowanie wszystkich danych. (Nie sądzęReverse
, aby zastosowano jakieś optymalizacje w taki sposóbCount()
). Zauważ, że to buforowanie oznacza, że dane są odczytywane w całości przy pierwszym uruchomieniu iteracji, podczasFastReverse
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:
Czego byś się spodziewał, gdybyś próbował powtórzyć to w odwrotnej kolejności?
źródło
Przed użyciem
foreach
do iteracji odwróć listę za pomocąreverse
metody:źródło
myList
jest pomocne. IEnumerable.Reverse nie będzie tutaj działać.myList
wydaje się być typuSystem.Collections.Generic.List<System.Windows.Documents.List>
(lub innegoList
typu niestandardowego ), w przeciwnym razie ten kod nie działa: PCzasami 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;
Stosowanie:
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.
Stosowanie:
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
źródło
Jeśli używasz List <T>, możesz również użyć tego kodu:
Jest to metoda, która sama w sobie zapisuje listę w odwrotnej kolejności.
Teraz foreach:
Wynik to:
źródło
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):
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).
źródło
Nie. ForEach po prostu wykonuje iterację kolekcji dla każdego elementu, a zamówienie zależy od tego, czy używa IEnumerable, czy GetEnumerator ().
źródło
Opierając się słabo na ładnej odpowiedzi Jona Skeeta , może to być wszechstronne:
A następnie użyj jako
źródło
Użyłem tego kodu, który zadziałał
źródło
.Reverse()
faktycznie modyfikuje listę.Działa to całkiem nieźle
źródło