Jest to najpopularniejszy sposób (wydaje mi się) sprawdzania, czy wartość znajduje się w tablicy:
for (int x : array)
{
if (x == value)
return true;
}
return false;
Jednak w książce, którą czytałem wiele lat temu, prawdopodobnie przez Wirtha lub Dijkstry, powiedziano, że ten styl jest lepszy (w porównaniu do pętli while z wyjściem w środku):
int i = 0;
while (i < array.length && array[i] != value)
i++;
return i < array.length;
W ten sposób dodatkowy warunek wyjścia staje się jawną częścią niezmiennika pętli, nie ma żadnych ukrytych warunków i wychodzi z pętli, wszystko jest bardziej oczywiste i bardziej w sposób ustrukturyzowany. Generalnie wolałem ten ostatni wzór, gdy tylko było to możliwe, i użyłem for
pętli do iteracji tylko od a
do b
.
A jednak nie mogę powiedzieć, że pierwsza wersja jest mniej przejrzysta. Może jest to jeszcze bardziej zrozumiałe i łatwiejsze do zrozumienia, przynajmniej dla bardzo początkujących. Więc wciąż zadaję sobie pytanie, które z nich jest lepsze?
Może ktoś może podać uzasadnienie na korzyść jednej z metod?
Aktualizacja: To nie jest kwestia punktów zwrotnych z wielu funkcji, lambdów lub znalezienia elementu w tablicy jako takiej. Chodzi o to, jak pisać pętle z bardziej złożonymi niezmiennikami niż pojedyncza nierówność.
Aktualizacja: OK, widzę sens ludzi, którzy odpowiadają i komentują: Włączyłem tutaj pętlę foreach, która sama w sobie jest już znacznie bardziej przejrzysta i czytelna niż pętla while. Nie powinnam tego robić. Ale jest to również interesujące pytanie, więc zostawmy je takie, jakie są: pętla foreach i dodatkowy warunek w środku, lub pętla while z wyraźną niezmiennikiem pętli i późniejszym warunkiem. Wydaje się, że wygrywa pętla foreach z warunkiem i wyjściem / przerwaniem. Stworzę dodatkowe pytanie bez pętli foreach (dla listy połączonej).
źródło
collection.contains(foo)
Odpowiedzi:
Myślę, że w przypadku prostych pętli, takich jak te, standardowa pierwsza składnia jest znacznie jaśniejsza. Niektórzy uważają, że wielokrotne zwroty są mylące lub pachną kodem, ale w przypadku tak małego fragmentu kodu nie sądzę, aby był to prawdziwy problem.
To staje się nieco bardziej dyskusyjne dla bardziej złożonych pętli. Jeśli zawartość pętli nie mieści się na ekranie i ma kilka zwrotów w pętli, należy argumentować, że wiele punktów wyjścia może utrudnić utrzymanie kodu. Na przykład, jeśli musiałbyś upewnić się, że jakaś metoda utrzymywania stanu została uruchomiona przed wyjściem z funkcji, łatwo byłoby pominąć dodanie jej do jednej z instrukcji return i spowodowałbyś błąd. Jeśli wszystkie warunki końcowe można sprawdzić w pętli while, masz tylko jeden punkt wyjścia i możesz dodać ten kod po nim.
To powiedziawszy, szczególnie w przypadku pętli dobrze jest wypróbować jak najwięcej logiki w oddzielnych metodach. Pozwala to uniknąć wielu przypadków, w których druga metoda miałaby zalety. Pętle Lean z wyraźnie oddzieloną logiką będą miały większe znaczenie niż który z tych stylów używasz. Ponadto, jeśli większość kodu źródłowego aplikacji używa jednego stylu, powinieneś pozostać przy tym stylu.
źródło
To jest łatwe.
Prawie nic nie jest ważniejsze niż czytelność dla czytelnika. Pierwszy wariant, który znalazłem, był niezwykle prosty i przejrzysty.
Druga „ulepszona” wersja, musiałam przeczytać kilka razy i upewnić się, że wszystkie warunki brzegowe są prawidłowe.
Istnieje ZERO DOUBT, który ma lepszy styl kodowania (pierwszy jest znacznie lepszy).
Teraz - to, co jest CZYSTE dla ludzi, może różnić się w zależności od osoby. Nie jestem pewien, czy istnieją w tym celu obiektywne standardy (choć publikowanie na forum takim jak ten i uzyskiwanie informacji od różnych osób może pomóc).
W tym konkretnym przypadku mogę jednak powiedzieć, dlaczego pierwszy algorytm jest bardziej przejrzysty: wiem, jak wygląda i działa iteracja C ++ nad składnią kontenera. Zinternalizowałem to. Ktoś UNFAMILIAR (jego nowa składnia) z tą składnią może preferować drugą odmianę.
Ale kiedy poznasz i zrozumiesz tę nową składnię, jest to podstawowa koncepcja, której możesz po prostu użyć. W przypadku iteracji pętli (druga) należy dokładnie sprawdzić, czy użytkownik PRAWIDŁOWO sprawdza, czy wszystkie warunki brzegowe zapętlają całą tablicę (np. Mniej niż w miejsce mniejszego lub równego tego samego indeksu używanego do testowania i do indeksowania itp.).
źródło
longerLength = true
, a następniereturn longerLength
.length
. Jeśli faktycznie został zadeklarowany jako tablica, a nie wskaźnik, mogliby użyćsizeof
, lub gdyby byłastd::array
, poprawną funkcją składową jest tosize()
, że nie malength
właściwości.sizeof
byłby w bajtach ... Najbardziej ogólny od czasu C ++ 17std::size()
.Nie do końca. Zmienna
i
występuje na zewnątrz pętli, a tu a zatem część zakresu zewnętrznej, podczas gdy (pun przeznaczone)x
zfor
istnieje -loop tylko w zakresie pętli. Zakres jest jednym z bardzo ważnych sposobów na wprowadzenie struktury do programowania.źródło
Dwie pętle mają inną semantykę:
Pierwsza pętla po prostu odpowiada na proste pytanie typu tak / nie: „Czy tablica zawiera obiekt, którego szukam?” Robi to w możliwie najkrótszy sposób.
Druga pętla odpowiada na pytanie: „Jeśli tablica zawiera szukany obiekt, jaki jest indeks pierwszego dopasowania?” Ponownie robi to w możliwie najkrótszy sposób.
Ponieważ odpowiedź na drugie pytanie zawiera ściśle więcej informacji niż odpowiedź na pierwsze, możesz wybrać odpowiedź na drugie pytanie, a następnie uzyskać odpowiedź na pierwsze pytanie. W
return i < array.length;
każdym razie tak właśnie działa linia .Uważam, że zwykle najlepiej jest po prostu użyć narzędzia, które pasuje do tego celu, chyba że możesz ponownie użyć już istniejącego, bardziej elastycznego narzędzia. To znaczy:
bool
zmienną i break, również jest w porządku. (Unika drugiejreturn
instrukcji, odpowiedź jest dostępna w zmiennej zamiast funkcji return).std::find
jest w porządku (ponowne użycie kodu!).bool
nie”, nie jest.źródło
Zasugeruję trzecią opcję w ogóle:
Istnieje wiele różnych powodów iteracji po tablicy: Sprawdź, czy istnieje konkretna wartość, przekształć tablicę w inną tablicę, oblicz wartość zagregowaną, odfiltruj niektóre wartości z tablicy ... Jeśli używasz zwykłej pętli for, nie jest jasne na pierwszy rzut oka, w jaki sposób używana jest pętla for. Jednak większość współczesnych języków ma bogate interfejsy API w swoich strukturach tablicowych, które czynią te różne zamiary bardzo wyraźnymi.
Porównaj przekształcanie jednej tablicy w drugą za pomocą pętli for:
i za pomocą
map
funkcji w stylu JavaScript :Lub sumując tablicę:
przeciw:
Jak długo zajmuje ci zrozumienie, co to robi?
przeciw
We wszystkich trzech przypadkach, mimo że pętla for jest z pewnością czytelna, musisz poświęcić kilka chwil, aby dowiedzieć się, jak używana jest pętla for i sprawdzić, czy wszystkie liczniki i warunki wyjścia są poprawne. Nowoczesne funkcje w stylu lambda sprawiają, że cele pętli są wyjątkowo wyraźne i wiadomo na pewno, że wywoływane funkcje API są poprawnie zaimplementowane.
Większość współczesnych języków, w tym JavaScript , Ruby , C # i Java , używa tego stylu funkcjonalnej interakcji z tablicami i podobnymi kolekcjami.
Ogólnie rzecz biorąc, chociaż nie sądzę, aby korzystanie z pętli było koniecznie niewłaściwe i jest to kwestia osobistego gustu, zdecydowanie sprzyjam używaniu tego stylu pracy z tablicami. Wynika to szczególnie z większej przejrzystości w określaniu, co robi każda pętla. Jeśli twój język ma podobne funkcje lub narzędzia w swoich standardowych bibliotekach, sugeruję rozważenie przyjęcia tego stylu!
źródło
array.find
nasuwa się pytanie, jak mamy wówczas do omówienia najlepszego sposobu wdrożeniaarray.find
. Jeśli nie używasz sprzętu z wbudowanąfind
operacją, musimy tam napisać pętlę.find
w ich standardowych bibliotekach. Niewątpliwie biblioteki te implementująfind
i używają ich pętli dla pętli, ale to właśnie robi dobra funkcja: oddziela szczegóły techniczne od konsumenta funkcji, pozwalając programiście nie myśleć o tych szczegółach. Tak więc, mimo żefind
prawdopodobnie jest implementowany z pętlą for, nadal pomaga uczynić kod bardziej czytelnym, a ponieważ często znajduje się w standardowej bibliotece, użycie go nie powoduje znaczącego obciążenia ani ryzyka.Wszystko sprowadza się do dokładnie tego, co należy rozumieć przez „lepiej”. Dla praktycznych programistów ogólnie oznacza to wydajność - tj. W tym przypadku wyjście bezpośrednio z pętli pozwala uniknąć jednego dodatkowego porównania, a zwrócenie stałej logicznej pozwala uniknąć podwójnego porównania; to oszczędza cykle. Dijkstra jest bardziej zainteresowany tworzeniem kodu, który będzie łatwiejszy do udowodnienia . [wydawało mi się, że edukacja CS w Europie traktuje „sprawdzanie poprawności kodu” znacznie poważniej niż edukacja CS w Stanach Zjednoczonych, gdzie siły ekonomiczne zdominowały praktykę kodowania]
źródło