Muszę przejść wstecz przez tablicę, więc mam taki kod:
for (int i = myArray.Length - 1; i >= 0; i--)
{
// Do something
myArray[i] = 42;
}
Czy jest lepszy sposób na zrobienie tego?
Aktualizacja: Miałem nadzieję, że być może C # ma wbudowany mechanizm, taki jak:
foreachbackwards (int i in myArray)
{
// so easy
}
Aktualizacja 2: są lepsze sposoby. Rune zdobywa nagrodę z:
for (int i = myArray.Length; i-- > 0; )
{
//do something
}
//or
for (int i = myArray.Length; i --> 0; )
{
// do something
}
który wygląda jeszcze lepiej w zwykłym C (dzięki Twotymz):
for (int i = lengthOfArray; i--; )
{
//do something
}
Odpowiedzi:
Choć wprawdzie trochę niejasne, powiedziałbym, że najbardziej typograficznie przyjemnym sposobem na zrobienie tego jest
źródło
W C ++ masz wybór między iteracją przy użyciu iteratorów lub indeksów. W zależności od tego, czy masz zwykłą tablicę, czy plik
std::vector
, używasz różnych technik.Korzystanie z std :: vector
Korzystanie z iteratorów
C ++ pozwala to zrobić za pomocą
std::reverse_iterator:
Korzystanie z indeksów
Niepodpisany integralną typ zwracany przez
std::vector<T>::size
to nie zawszestd::size_t
. Może być większy lub mniejszy. Ma to kluczowe znaczenie dla działania pętli.To działa, ponieważ wartości typów całkowitych bez znaku są definiowane za pomocą modulo ich liczby bitów. Tak więc, jeśli ustawiasz
-N
, kończysz na(2 ^ BIT_SIZE) -N
Korzystanie z tablic
Korzystanie z iteratorów
Używamy
std::reverse_iterator
do iteracji.Korzystanie z indeksów
Możemy bezpiecznie użyć
std::size_t
tutaj, w przeciwieństwie do powyższego, ponieważsizeof
zawsze wracastd::size_t
z definicji.Unikanie pułapek, w których do wskaźników zastosowano sizeof
W rzeczywistości powyższy sposób określania rozmiaru tablicy jest do niczego. Jeśli a jest w rzeczywistości wskaźnikiem zamiast tablicą (co zdarza się dość często i początkujący będą go mylić), po cichu zawiedzie. Lepszym sposobem jest użycie poniższego, co zakończy się niepowodzeniem w czasie kompilacji, jeśli podany zostanie wskaźnik:
Działa poprzez pobranie najpierw rozmiaru przekazanej tablicy, a następnie deklarację zwrócenia odwołania do tablicy typu char o tym samym rozmiarze.
char
jest zdefiniowana jako posiadającasizeof
: 1. Więc zwrócona tablica będzie miała wartośćsizeof
: N * 1, czyli to, czego szukamy, tylko z obliczaniem czasu kompilacji i zerowym narzutem czasu wykonania.Zamiast robić
Zmień swój kod tak, aby działał teraz
źródło
end
ibegin
w odwrotnej kolejności?W C # , używając Visual Studio 2005 lub nowszego, wpisz „forr” i naciśnij [TAB] [TAB] . To rozwinie się do
for
pętli, która przechodzi wstecz przez kolekcję.Tak łatwo się pomylić (przynajmniej dla mnie), że pomyślałem, że umieszczenie tego fragmentu będzie dobrym pomysłem.
To powiedziawszy, podoba mi się
Array.Reverse()
/Enumerable.Reverse()
a następnie lepiej iteruję do przodu - wyraźniej określają zamiar.źródło
Zawsze wolałbym czysty kod zamiast „ przyjemnego typograficznie ” kodu. Dlatego zawsze używałbym:
Można to uznać za standardowy sposób wykonywania pętli wstecz.
Tylko moje dwa centy...
źródło
W C # przy użyciu Linq :
źródło
To zdecydowanie najlepszy sposób dla każdej tablicy, której długość jest typem całkowitym ze znakiem. W przypadku tablic, których długości są typami całkowitymi bez znaku (np.
std::vector
W C ++), należy nieco zmodyfikować warunek końcowy:Jeśli właśnie powiedziałeś
i >= 0
, jest to zawsze prawdziwe dla liczby całkowitej bez znaku, więc pętla będzie nieskończoną pętlą.źródło
Dla mnie wygląda dobrze. Jeśli indeksator był bez znaku (uint itp.), Być może będziesz musiał wziąć to pod uwagę. Nazwij mnie leniwym, ale w tym (bez znaku) przypadku mogę po prostu użyć zmiennej przeciwnej:
(właściwie nawet tutaj trzeba uważać na przypadki takie jak arr.Length = uint.MaxValue ... może a! = gdzieś ... oczywiście, to jest bardzo mało prawdopodobny przypadek!)
źródło
W CI lubię to robić:
Przykład C # dodany przez MusiGenesis:
źródło
Najlepszym sposobem na zrobienie tego w C ++ jest prawdopodobnie użycie iteratorów (lub lepiej zakresów), które będą leniwie przekształcać sekwencję podczas przechodzenia.
Gruntownie,
Wyświetla zakres „zakres” (tutaj jest pusty, ale jestem prawie pewien, że możesz samodzielnie dodawać elementy) w odwrotnej kolejności. Oczywiście zwykłe iterowanie zakresu nie jest zbyt przydatne, ale przekazanie tego nowego zakresu algorytmom i tak dalej jest całkiem fajne.
Ten mechanizm można również wykorzystać do znacznie potężniejszych zastosowań:
Leniwie obliczy zakres "zakres", gdzie funkcja "f" jest zastosowana do wszystkich elementów, elementy, dla których "p" nie jest prawdziwe, są usuwane, a na koniec wynikowy zakres jest odwracany.
Składnia potoku jest najbardziej czytelną wersją IMO, biorąc pod uwagę jej wrostek. Aktualizacja biblioteki Boost.Range oczekująca na recenzję implementuje to, ale jest to całkiem proste, aby zrobić to również samodzielnie. Jeszcze fajniej jest generować wbudowaną funkcję f i predykat p z lambda DSEL.
źródło
źródło
Wolę pętlę while. Jest to dla mnie bardziej jasne niż dekrementacja
i
w stanie pętli forźródło
Użyłbym kodu w oryginalnym pytaniu, ale jeśli naprawdę chciałbyś użyć foreach i mieć indeks całkowity w C #:
źródło
Spróbuję tutaj odpowiedzieć na własne pytanie, ale to też mi się nie podoba:
źródło
UWAGA: Ten post okazał się znacznie bardziej szczegółowy i dlatego nie na temat, przepraszam.
Mając to na uwadze, moi rówieśnicy czytają go i uważają, że „gdzieś” jest wartościowy. Ten wątek nie jest odpowiednim miejscem. Byłbym wdzięczny za twoją opinię na temat tego, gdzie to powinno iść (jestem nowy w tej witrynie).
W każdym razie jest to wersja C # w .NET 3.5, która jest niesamowita, ponieważ działa na każdym typie kolekcji przy użyciu zdefiniowanej semantyki. Jest to domyślna miara (ponowne użycie!), A nie wydajność lub minimalizacja cyklu procesora w większości typowych scenariuszy deweloperskich, chociaż wydaje się, że nigdy tak nie dzieje się w świecie rzeczywistym (przedwczesna optymalizacja).
*** Metoda rozszerzenia działająca na dowolnym typie kolekcji i przyjmująca delegata akcji oczekująca pojedynczej wartości typu, wszystkie wykonywane na każdym elemencie w odwrotnej kolejności **
Wymagania 3.5:
Starsze wersje .NET, czy chcesz lepiej poznać wewnętrzne funkcje Linq? Czytaj dalej… Albo nie…
ZAŁOŻENIE: W systemie typów .NET typ Array dziedziczy po interfejsie IEnumerable (nie z ogólnego IEnumerable tylko IEnumerable).
To wszystko, czego potrzebujesz, aby iterować od początku do końca, jednak chcesz przejść w przeciwnym kierunku. Ponieważ IEnumerable działa na tablicy typu „object”, każdy typ jest prawidłowy,
ŚRODEK KRYTYCZNY: Zakładamy, że jeśli można przetworzyć dowolną sekwencję w odwrotnej kolejności, która jest „lepsza”, to można to zrobić tylko na liczbach całkowitych.
Rozwiązanie a dla .NET CLR 2.0-3.0:
Opis: zaakceptujemy każde wystąpienie implementujące IEnumerable z wymaganiem, że każde zawarte w nim wystąpienie jest tego samego typu. Więc jeśli otrzymamy tablicę, cała tablica zawiera instancje typu X. Jeśli jakiekolwiek inne instancje są typu! = X, wyrzucany jest wyjątek:
Usługa singleton:
public class ReverserService {private ReverserService () {}
[TestFixture] public class Testing123 {
źródło