Czasami potrzebuję pętli, które wymagają takiej przerwy:
for(int i=0;i<array.length;i++){
//some other code
if(condition){
break;
}
}
Czuję się nieswojo z pisania
if(condition){
break;
}
ponieważ zużywa 3 linie kodu. Odkryłem, że pętlę można przepisać jako:
↓
for(int i=0;i<array.length && !condition;i++){
//some other code
}
Moje pytanie brzmi: czy dobrą praktyką jest przeniesienie warunku do pola warunku, aby w miarę możliwości zmniejszyć liczbę kodów?
coding-style
loops
ocomfd
źródło
źródło
condition
w szczególności.for(int i=0;i<array.length && !condition;i++)
jest stosunkowo rzadka i może zostać przeoczona przez kogoś, kto tylko przegląda kod; może to być dobry przypadek użycia pętliwhile
lubdo while
, która częściej ma wiele warunków przerwania w definicji pętli.Odpowiedzi:
Te dwa przykłady, które podałeś, nie są funkcjonalnie równoważne. W oryginale test warunków jest wykonywany po sekcji „jakiś inny kod”, podczas gdy w zmodyfikowanej wersji jest on wykonywany najpierw na początku treści pętli.
Kod nigdy nie powinien być przepisywany wyłącznie w celu zmniejszenia liczby wierszy. Oczywiście jest to miły bonus, gdy tak działa, ale nigdy nie należy tego robić kosztem czytelności lub poprawności.
źródło
Nie kupuję argumentu, że „zużywa 3 linie kodu” i dlatego jest zły. W końcu możesz po prostu napisać to jako:
i po prostu zużyj jedną linię.
Jeśli
if
pojawia się w połowie pętli, to oczywiście musi istnieć jako osobny test. Jeśli jednak ten test pojawi się na końcu bloku, utworzono pętlę, która ma test kontynuacji na obu końcach pętli, co może zwiększyć złożoność kodu. Pamiętaj jednak, że czasamiif (condition)
test może mieć sens dopiero po wykonaniu bloku.Ale zakładając, że tak nie jest, przyjmując inne podejście:
warunki wyjścia są utrzymywane razem, co może uprościć rzeczy (zwłaszcza jeśli „ jakiś inny kod ” jest długi).
Oczywiście nie ma tu właściwej odpowiedzi. Więc pamiętaj o idiomach języka, jakie są konwencje kodowania twojego zespołu itp. Ale w sumie przyjąłbym podejście z pojedynczym testem, gdy ma to sens funkcjonalny.
źródło
Tego rodzaju pytanie wywołało debatę prawie tak długo, jak trwa programowanie. Aby rzucić kapelusz na ring, wybrałbym wersję z
break
warunkiem i oto dlaczego (w tym konkretnym przypadku):for
Pętla jest właśnie tam, aby określić wartości iteracji w pętli. Kodowanie warunku zerwania, które po prostu zamazuje logikę IMO.Oddzielne
break
sprawia, że staje się jasne, że podczas normalnego przetwarzania wartości są takie, jak określono wfor
pętli i przetwarzanie powinno być kontynuowane, z wyjątkiem sytuacji, w której wystąpi warunek.Jako trochę tła zacząłem kodować a
break
, potem przez lata wprowadzałem ten stan wfor
pętlę (pod kierunkiem wyjątkowego programisty), a potem wróciłem dobreak
stylu, ponieważ wydawał się on znacznie czystszy (i był dominującym stylem w różne tony kodu, które konsumowałem).To kolejna z tych świętych wojen pod koniec dnia - niektórzy twierdzą, że nigdy nie powinieneś sztucznie wychodzić z pętli, w przeciwnym razie nie jest to prawdziwa pętla. Rób, co uważasz za czytelne (zarówno dla siebie, jak i innych). Jeśli naprawdę wolisz ograniczać liczbę naciśnięć klawiszy, zagraj w golfa w weekend - piwo opcjonalne.
źródło
for
pętle są używane, gdy znana jest liczba iteracji (np. Gdy nie ma warunku zerwania, ale koniec tablicy / listy), awhile
kiedy nie. Wydaje się, że tak jest w przypadkuwhile
pętli. Jeśli chodzi o czytelność,idx+=1
wewnętrzna pętla jest znacznie czytelniejsza niżif/else
blokfor(int i=0; i<something;i++)
pętli, że wydaje mi się, że połowa programistów tak naprawdę nie pamięta, żefor
pętle mogą być używane inaczej, a zatem trudno jest im zrozumieć tę&&condition
część.for(;;)
...for
pętle służą do iteracji nad czymś 1 - nie są to po prostu lol-pull-some-random-stuff-from-the-body-and-put-them-in-the-loop-header - trzy wyrażenia mają bardzo konkretne znaczenia:Chodzi o to, aby oddzielić obsługę iteracji ( jak iterować) od logiki wewnątrz pętli ( co robić w każdej iteracji). Wiele języków zwykle ma pętlę dla każdego, która odciąża cię od szczegółów iteracji, ale nawet jeśli twój język nie ma takiej konstrukcji lub jeśli nie można jej użyć w twoim scenariuszu - nadal powinieneś ograniczyć nagłówek pętli do obsługi iteracji.
Musisz więc zadać sobie pytanie - czy Twój stan dotyczy obsługi iteracji czy logiki wewnątrz pętli? Możliwe, że chodzi o logikę wewnątrz pętli - dlatego należy to sprawdzić w pętli, a nie w nagłówku.
1 W przeciwieństwie do innych pętli, które „czekają”, aż coś się wydarzy. Pętla A
for
/foreach
powinna mieć pojęcie „listy” elementów do iteracji - nawet jeśli ta lista jest leniwa, nieskończona lub abstrakcyjna.źródło
someFunction(array[i])
. Teraz obie części warunku dotyczą iteracji, i warto je połączyć&&
w nagłówku pętli.for
pętle w sercu są licznikami, a nie pętlami nad listą, i jest to obsługiwane przez składnięfor
we wcześniejszych językach (języki te często miałyfor i = 1 to 10 step 2
składnię istep
polecenie - pozwalając nawet na wartość początkową, która nie jest indeksem pierwszego element - wskazówki w kontekście liczbowym, a nie w kontekście listy). Tylko przypadek użycia że myślę podporyfor
pętle jak tylko iteracji na listę jest parallelising, a to wymaga wyraźnej składni teraz w każdym razie, aby nie złamać kod robi, na przykład rodzaj.Polecam używać wersji z
if (condition)
. Jest bardziej czytelny i łatwiejszy do debugowania. Napisanie 3 dodatkowych wierszy kodu nie złamie ci palców, ale ułatwi kolejnej osobie zrozumienie twojego kodu.źródło
naming things
, aby zrozumieć, co się dzieje i kiedy warunek złamania jest spełniony.Jeśli iterujesz w kolekcji, w której niektóre elementy powinny zostać pominięte, polecam nową opcję:
Zarówno Java, jak i C # sprawiają, że jest to stosunkowo trywialne. Nie zdziwiłbym się, gdyby C ++ stworzył fantazyjny iterator, który pomija pewne elementy, które nie mają zastosowania. Ale tak naprawdę jest to opcja tylko wtedy, gdy twój język to obsługuje, a powodem tego jest
break
to, że istnieją warunki, czy przetwarzasz element, czy nie.W przeciwnym razie istnieje bardzo dobry powód, aby uczynić twój stan częścią pętli for - zakładając, że jego początkowa ocena jest poprawna.
Pracowałem nad kodem, w którym twoja pętla for zajmuje kilkaset wierszy z kilkoma warunkowymi i skomplikowaną matematyką. Problemy, z którymi się spotkasz to:
break
polecenia ukryte w logice są trudne do znalezienia, a czasem zaskakująceW takiej sytuacji zalecam następujące wytyczne w kolejności preferencji:
break
jeśli to możliwebreak
w górnej części pętli, jeśli to w ogóle możliweTe wytyczne zawsze podlegają poprawności twojego kodu. Wybierz opcję, która najlepiej oddaje intencję i poprawia przejrzystość kodu.
źródło
continue
wyrażeń, ale nie widzę, w czym bardzo pomagająbreak
.takeWhile
Filtr może zastąpićbreak
instrukcje. Jeśli masz taki - na przykład Java dostaje je tylko w Jave 9 (nie dlatego, że samemu trudno jest go wdrożyć)Zdecydowanie zalecam podejście najmniej zaskakujące, chyba że uzyskasz znaczącą korzyść, robiąc inaczej.
Ludzie nie czytają każdej litery podczas czytania słowa i nie czytają każdego słowa podczas czytania książki - jeśli są biegli w czytaniu, patrzą na kontury słów i zdań i pozwalają mózgowi wypełnić resztę .
Dlatego od czasu do czasu programista zakłada, że jest to tylko standard pętli, a nawet na to nie patrzy:
Jeśli chcesz używać tego stylu niezależnie od tego, zalecam zmianę części,
for(int i=0;i<
a;i++)
to powie mózgowi czytelnika, że jest to standard pętli.Kolejnym powodem, dla którego warto iść
if
-break
jest to, że nie zawsze możesz zastosować swoje podejście zfor
warunkiem. Musisz użyćif
-break
gdy warunek przerwania jest zbyt skomplikowany, aby ukryć się wfor
instrukcji, lub opiera się na zmiennych, które są dostępne tylko wfor
pętli.Więc jeśli zdecydujesz się pójść z
if
-break
jesteś objęty ubezpieczeniem. Ale jeśli zdecydujesz się iść zfor
-conditions, trzeba użyć kombinacjiif
-break
ifor
-conditions. Aby zachować spójność, będziesz musiał przesuwać warunki do przodu i do tyłu międzyfor
warunkami iif
-break
ilekroć warunki się zmienią.źródło
Twoja transformacja zakłada, że cokolwiek
condition
zostanie ocenionetrue
po wejściu do pętli. Nie możemy komentować poprawności tego w tym przypadku, ponieważ nie jest on zdefiniowany.Po osiągnięciu sukcesu w „zmniejszeniu linii kodu” możesz przejść do przeglądania
i zastosuj tę samą transformację, dochodząc do
Co jest nieprawidłową transformacją, ponieważ teraz bezwarunkowo robisz
further code
to, czego wcześniej nie robiłeś.Znacznie bezpieczniejszym schematem transformacji jest wyodrębnienie
some other code
i ewaluacjacondition
do oddzielnej funkcjiźródło
TL; DR Rób to, co najlepiej czyta w danych okolicznościach
Weźmy ten przykład:
W tym przypadku nie chcemy, aby jakikolwiek kod w pętli for był wykonywany, jeśli
something
jest prawdziwy, więc przeniesienie testusomething
do pola warunku jest rozsądne.Z drugiej strony, w zależności od tego, czy
something
można ustawić wartośćtrue
przed pętlą, ale nie w jej obrębie, może to zapewnić większą przejrzystość:Teraz wyobraź sobie:
Wysyłamy tam bardzo wyraźną wiadomość. Jeśli stanie
something
się prawdą podczas przetwarzania tablicy, porzuć w tym momencie całą operację. Aby przenieść czek do pola warunku, musisz:Prawdopodobnie „wiadomość” stała się mętna, a my sprawdzamy stan awarii w dwóch miejscach - co, jeśli stan awarii zmieni się i zapomnimy jedno z tych miejsc?
Istnieje oczywiście wiele innych kształtów i warunków dla pętli for, wszystkie z własnymi niuansami.
Napisz kod, aby dobrze czytał (jest to oczywiście nieco subiektywne) i zwięźle. Poza drakońskim zbiorem wytycznych dotyczących kodowania wszystkie „reguły” mają wyjątki. Pisz, co czujesz, najlepiej wyraża podejmowanie decyzji i minimalizuj szanse na przyszłe błędy.
źródło
Chociaż może to być atrakcyjne, ponieważ widzisz wszystkie warunki w nagłówku pętli i
break
może być mylące w dużych blokach,for
pętla jest wzorcem, w którym większość programistów oczekuje pętli w pewnym zakresie.Zwłaszcza, że to robisz, dodanie dodatkowego warunku przerwania może powodować zamieszanie i trudniej jest sprawdzić, czy jest funkcjonalnie równoważny.
Często dobrą alternatywą jest użycie pętli
while
lubdo {} while
, która sprawdza oba warunki, zakładając, że tablica zawiera co najmniej jeden element.Korzystając z pętli, która sprawdza tylko warunek i nie uruchamia kodu z nagłówka pętli, bardzo jasno określasz, kiedy warunek jest sprawdzany oraz jaki jest faktyczny stan i jaki kod ma skutki uboczne.
źródło
for(...; i <= n; ...)
) faktycznie utrudnia odczytanie kodu, ponieważ mam zatrzymać się i pomyśleć o tym, co się tutaj dzieje, i może całkowicie spudłować ten warunek przy pierwszym przejeździe, ponieważ nie oczekuję, że tak się stanie. Dla mniefor
pętla oznacza, że zapętlasz się w pewnym zakresie, jeśli istnieją specjalne warunki wyjścia, należy to wyraźnie zaznaczyć za pomocąif
, lub możesz użyćwhile
.Jest to złe, ponieważ kod ma być odczytywany przez ludzi - nieidiomatyczne
for
pętle są często mylące z czytaniem, co oznacza, że błędy prawdopodobnie będą się tutaj ukrywać, ale oryginalny kod mógł zostać poprawiony przy jednoczesnym zachowaniu zarówno krótki i czytelny.Jeśli chcesz znaleźć wartość w tablicy (podany przykładowy kod jest nieco ogólny, ale równie dobrze może to być ten wzorzec), możesz po prostu jawnie spróbować użyć funkcji zapewnianej przez język programowania specjalnie do tego celu. Na przykład (pseudo-kod, ponieważ nie określono języka programowania, rozwiązania różnią się w zależności od konkretnego języka).
Powinno to wyraźnie skrócić rozwiązanie, jednocześnie upraszczając je, ponieważ kod mówi konkretnie, czego potrzebujesz.
źródło
Moim zdaniem kilka powodów mówi, że nie powinieneś.
do...while
pętli (niewhile
), ponieważ warunek jest sprawdzany po wykonaniu kodu.Ale oprócz tego rozważ to,
Czy naprawdę możesz to osiągnąć, dodając te warunki do tego
for
warunku?źródło
Myślę, że usunięcie
break
może być dobrym pomysłem, ale nie zapisywanie wierszy kodu.Zaletą pisania go bez
break
jest to, że warunek końcowy pętli jest oczywisty i wyraźny. Po zakończeniu pętli i wykonaniu następnego wiersza programu warunek pętlii < array.length && !condition
musi być fałszywy. Dlatego alboi
była większa lub równa długości tablicy, albocondition
jest prawdziwa.W wersji z
break
jest ukryta gotcha. Warunek pętli mówi, że pętla kończy się, gdy uruchomisz jakiś inny kod dla każdego poprawnego indeksu tablicy, ale w rzeczywistości istniejebreak
instrukcja, która mogłaby wcześniej zakończyć pętlę i nie zobaczysz jej, dopóki nie przejrzysz kodu pętli bardzo ostrożnie. Zautomatyzowane analizatory statyczne, w tym optymalizujące kompilatory, mogą mieć problemy z wydedukowaniem, jakie są końcowe warunki pętli.To był pierwotny powód, dla którego Edgar Dijkstra napisał „Goto uważane za szkodliwe”. Nie była to kwestia stylu: zerwanie z pętlą utrudnia formalne uzasadnienie stanu programu.
break;
W swoim życiu napisałem wiele stwierdzeń, a gdy byłem dorosły, napisałem nawetgoto
(aby przełamać wiele poziomów wewnętrznej pętli krytycznej pod względem wydajności, a następnie przejść do kolejnej gałęzi drzewa wyszukiwania). Jestem jednak znacznie bardziej prawdopodobne, aby napisać warunkowereturn
oświadczenie pętli (która nigdy nie działa kod po pętli i dlatego nie może zepsuć jego post-warunki) niżbreak
.Postscriptum
W rzeczywistości refaktoryzacja kodu w celu uzyskania takiego samego zachowania może wymagać większej liczby wierszy kodu. Twoje refaktoryzacja może być ważna dla konkretnego kodu lub jeśli możemy udowodnić, że
condition
zacznie się fałsz. Ale twój przykład jest równoważny w ogólnym przypadku:Jeśli jakiś inny kod nie jest jednowierszowy, możesz przenieść go do funkcji, aby się nie powtarzać, a następnie prawie przerobiłeś pętlę na funkcję rekurencyjną.
Rozumiem, że to nie był główny punkt twojego pytania i upraszczałeś to.
źródło
break
instrukcji, to wiesz, jaki jest warunek pętli i że pętla zatrzymuje się, gdy warunek ten jest fałszywy. Jeśli istniejebreak
? Następnie istnieje wiele sposobów zakończenia pętli, aw ogólnym przypadku stwierdzenie, kiedy jeden z nich może zatrzymać pętlę, dosłownie rozwiązuje problem zatrzymania. W praktyce bardziej obawiam się, że pętla wygląda tak, jakby robiła jedną rzecz, ale w rzeczywistości ktokolwiek napisał kod po tym, jak pętla przeoczyła przypadek krawędzi zakopany w jego złożonej logice. Rzeczy w pętli są trudne do przeoczenia.Tak, ale twoja składnia jest niekonwencjonalna i dlatego zła (tm)
Mam nadzieję, że Twój język obsługuje coś takiego
źródło
while
podkreśla potwora w kodzie - wyjątek inaczej ukryte w rutynowej / code przyziemne. Logika biznesowa jest z przodu i na środku, mechanika zapętlania jest uwzględniona. Unika reakcji „poczekaj minutę ...” nafor
alternatywną pętlę. Ta odpowiedź rozwiązuje problem. absolutnie głosuj.Jeśli obawiasz się, że zbyt skomplikowane
for
stwierdzenie jest trudne do odczytania, jest to z pewnością uzasadniona obawa. Problemem jest także marnowanie przestrzeni pionowej (choć mniej krytycznej).(Osobiście nie podoba mi się zasada, że
if
instrukcje muszą zawsze mieć nawiasy klamrowe, co jest w dużej mierze przyczyną zmarnowanej przestrzeni pionowej tutaj. Zauważ, że problemem jest marnowanie przestrzeni pionowej. Korzystanie z przestrzeni pionowej ze znaczącymi liniami nie powinno stanowić problemu.)Jedną z opcji jest podzielenie go na wiele linii. To jest zdecydowanie to, co powinieneś zrobić, jeśli używasz naprawdę złożonych
for
instrukcji, z wieloma zmiennymi i warunkami. (Jest to dla mnie lepsze wykorzystanie przestrzeni pionowej, dając jak najwięcej linii, co ma sens).Lepszym podejściem może być próba użycia bardziej deklaratywnej i mniej imperatywnej formy. Różni się to w zależności od języka lub konieczne może być napisanie własnych funkcji, aby włączyć tego rodzaju funkcje. To zależy również od tego, co
condition
faktycznie jest. Prawdopodobnie musi być w stanie się jakoś zmienić, w zależności od tego, co znajduje się w tablicy.źródło
for
stwierdzenia na wiele wierszy jest najlepszym pomysłem w całej tej dyskusjiJedna rzecz, o której zapomniano: kiedy zrywam z pętli (nie wykonuję wszystkich możliwych iteracji, bez względu na to, jak zaimplementowane), zwykle jest tak, że nie tylko chcę wyjść z pętli, ale chcę również wiedzieć, dlaczego tak się stało . Na przykład, jeśli szukam elementu X, nie tylko przerywam pętlę, ale chcę również wiedzieć, że X został znaleziony i gdzie on jest.
W takiej sytuacji stan poza pętlą jest całkiem naturalny. Więc zaczynam od
a w pętli napiszę
z pętlą for
Teraz sprawdzenie stanu w pętli jest całkiem naturalne. To, czy istnieje instrukcja „break”, zależy od tego, czy w pętli jest dalsze przetwarzanie, którego chcesz uniknąć. Jeśli potrzebne jest pewne przetwarzanie, a inne nie, możesz mieć coś takiego
gdzieś w twojej pętli.
źródło