Mój szef utrzymuje wspomnieć nonszalancko, że złe programiści używać break
i continue
w pętli.
Używam ich cały czas, ponieważ mają sens; pozwól, że pokażę ci inspirację:
function verify(object) {
if (object->value < 0) return false;
if (object->value > object->max_value) return false;
if (object->name == "") return false;
...
}
Chodzi o to, że najpierw funkcja sprawdza, czy warunki są poprawne, a następnie wykonuje rzeczywistą funkcjonalność. IMO to samo dotyczy pętli:
while (primary_condition) {
if (loop_count > 1000) break;
if (time_exect > 3600) break;
if (this->data == "undefined") continue;
if (this->skip == true) continue;
...
}
Myślę, że to ułatwia czytanie i debugowanie; ale nie widzę też wad.
goto
) są przydatne w niektórych przypadkach.Odpowiedzi:
Kiedy są używane na początku bloku, gdy wykonywane są pierwsze kontrole, działają jak warunki wstępne, więc dobrze.
Kiedy są używane w środku bloku, z pewnym kodem, działają jak ukryte pułapki, więc jest źle.
źródło
Możesz przeczytać artykuł Donalda Knutha Programowanie strukturalne z 1974 r., Przejdź do Oświadczeń , w których omawia różne zastosowania tych,
go to
które są strukturalnie pożądane. Obejmują one odpowiednik instrukcjibreak
icontinue
(wiele z tamtejszych zastosowańgo to
zostało opracowanych w bardziej ograniczone konstrukcje). Czy twój szef nazywa Knutha złym programistą?(Podane przykłady mnie interesują. Zazwyczaj
break
icontinue
nie podobają im się osoby, które lubią jedno wejście i jedno wyjście z dowolnego fragmentu kodu, a taka osoba również marszczy brwi w związku z wielomareturn
stwierdzeniami.)źródło
Break
,Continue
aExit
jako narzędzia w moim przybornika; Używam ich tam, gdzie ułatwia to podążanie za kodem, i nie używam ich tam, gdzie byłoby to trudniejsze do odczytania.Nie wierzę, że są źli. Pomysł, że są źli, pochodzi z dni programowania strukturalnego. Jest to związane z pojęciem, że funkcja musi mieć jeden punkt wejścia i jeden punkt wyjścia, tj. Tylko jeden
return
na funkcję.Ma to sens, jeśli twoja funkcja jest długa i jeśli masz wiele zagnieżdżonych pętli. Jednak twoje funkcje powinny być krótkie i powinieneś zawijać pętle i ich ciała w ich krótkie funkcje. Ogólnie rzecz biorąc, wymuszenie, aby funkcja miała jeden punkt wyjścia, może spowodować bardzo skomplikowaną logikę.
Jeśli twoja funkcja jest bardzo krótka, jeśli masz pojedynczą pętlę lub, w najgorszym wypadku, dwie zagnieżdżone pętle, a jeśli treść pętli jest bardzo krótka, to jest bardzo jasne, co robi a
break
lub acontinue
. Oczywiste jest również, co robi wielereturn
instrukcji.Zagadnienia te zostały omówione w „Czystym kodzie” Roberta C. Martina oraz w „Refaktoryzacji” Martina Fowlera.
źródło
CALL P(X, Y, &10)
, a w przypadku błędu funkcja może przekazać kontrolę do tej instrukcji, zamiast wracać do punktu wywołania.Źli programiści mówią absolutnie (tak jak Sith). Dobrzy programiści używają najczystszych możliwych rozwiązań ( wszystkie inne rzeczy są równe ).
Używanie często przerywania i kontynuowania utrudnia przestrzeganie kodu. Ale jeśli ich zastąpienie sprawia, że kod jest jeszcze trudniejszy do naśladowania, to jest to zła zmiana.
Podany przykład jest zdecydowanie sytuacją, w której przerwy i kontynuacje powinny zostać zastąpione czymś bardziej eleganckim.
źródło
Większość ludzi uważa, że to zły pomysł, ponieważ zachowanie nie jest łatwe do przewidzenia. Jeśli czytasz ten kod i widzisz,
while(x < 1000){}
że zakładasz, że będzie on działał do x> = 1000 ... Ale jeśli są przerwy w środku, to nie jest to prawdą, więc nie możesz naprawdę ufać swojemu zapętlenie ...To ten sam powód, dla którego ludzie nie lubią GOTO: pewnie, można go dobrze używać, ale może także prowadzić do godawful kodu spaghetti, w którym kod przeskakuje losowo z sekcji do sekcji.
Dla mnie, gdybym miał zrobić pętlę, która przerwała więcej niż jeden warunek, zrobiłbym
while(x){}
wtedy X przełączając na false, gdy musiałem się wyrwać. Ostateczny wynik byłby taki sam, a każdy czytający kod wiedziałby, aby przyjrzeć się bliżej rzeczom, które zmieniły wartość X.źródło
while(notDone){ }
podejście.break;
zx=false;
nie czyni kod bardziej jasne. Nadal musisz przeszukać ciało pod kątem tego oświadczenia. A w przypadkux=false;
musisz sprawdzić, czy niex=true;
spadnie to bardziej.while (x < 1000)
, zakładam, że będzie działać 1000 razy”. Jest wiele powodów, dla których jest to fałsz, nawet jeślix
początkowo wynosi zero. Na przykład, kto mówi, żex
jest zwiększany dokładnie raz podczas pętli i nigdy nie jest modyfikowany w żaden inny sposób? Nawet dla twojego własnego założenia, tylko dlatego, że coś ustawiax >= 1000
nie oznacza, że pętla się skończy - może zostać ustawiona z powrotem w zakresie, zanim warunek zostanie sprawdzony.Tak, możesz [ponownie] pisać programy bez instrukcji break (lub wraca ze środka pętli, które robią to samo). Ale może być konieczne wprowadzenie dodatkowych zmiennych i / lub powielanie kodu, które zwykle utrudniają zrozumienie programu. Z tego powodu Pascal (język programowania) był bardzo zły, szczególnie dla początkujących programistów. Twój szef zasadniczo chce, żebyś programował w strukturach kontrolnych Pascala. Gdyby Linus Torvalds był w twoich butach, prawdopodobnie pokazałby szefowi środkowy palec!
Jest wynik informatyki zwany hierarchią struktur kontrolnych Kosaraju, która sięga 1973 roku i jest wspomniana w (bardziej) znanym artykule Knutha na temat gotos z 1974 roku. ). S. Rao Kosaraju udowodnił w 1973 r., Że nie jest możliwe przepisanie wszystkich programów, które mają wielopoziomowe podziały głębokości n na programy o głębokości przerwania mniejszej niż n bez wprowadzenia dodatkowych zmiennych. Powiedzmy jednak, że jest to wynik czysto teoretyczny. (Po prostu dodaj kilka dodatkowych zmiennych ?! Z pewnością możesz to zrobić, aby zadowolić swojego szefa ...)
O wiele ważniejsze z punktu widzenia inżynierii oprogramowania jest najnowszy artykuł Erica S. Robertsa z 1995 r. Zatytułowany Loop Exits and Structured Programming: Reopening the Debate ( http://cs.stanford.edu/people/eroberts/papers/SIGCSE- 1995 / LoopExits.pdf ). Roberts podsumowuje kilka badań empirycznych przeprowadzonych przez innych przed nim. Na przykład, gdy grupa studentów typu CS101 została poproszona o napisanie kodu dla funkcji realizującej wyszukiwanie sekwencyjne w tablicy, autor badania powiedział o tych studentach, którzy użyli break / return / goto do wyjścia z sekwencyjna pętla wyszukiwania, gdy element został znaleziony:
Roberts mówi również, że:
Tak, możesz być bardziej doświadczony niż studenci CS101, ale bez użycia instrukcji break (lub równoważnie return / goto ze środka pętli), w końcu napiszesz kod, który przy nominalnej ładnej strukturze jest wystarczająco włochaty pod względem dodatkowej logiki zmienne i powielanie kodu, że ktoś, prawdopodobnie ty, wprowadzi do niego błędy logiczne, starając się podążać za stylem twojego szefa.
Powiem też tutaj, że artykuł Roberta jest o wiele bardziej dostępny dla przeciętnego programisty, więc lepiej najpierw przeczytać niż Knutha. Jest także krótszy i obejmuje węższy temat. Prawdopodobnie mógłbyś nawet polecić to swojemu szefowi, nawet jeśli jest to kierownictwo, a nie typ CS.
źródło
Nie rozważam zastosowania żadnej z tych złych praktyk, ale użycie ich zbyt wiele w tej samej pętli powinno uzasadnić przemyślenie logiki używanej w tej pętli. Używaj ich oszczędnie.
źródło
Podany przykład nie wymaga przerw ani kontynuacji:
Mój „problem” z 4 liniami w twoim przykładzie polega na tym, że wszystkie są na tym samym poziomie, ale robią różne rzeczy: niektóre przerwy, niektóre kontynuują ... Musisz przeczytać każdą linię.
W moim podejściu zagnieżdżonym, im głębiej idziesz, tym bardziej „użyteczny” staje się kod.
Ale jeśli głęboko w środku znajdziesz powód, aby zatrzymać pętlę (inny niż warunek podstawowy), skorzystaj z przerwy lub powrotu. Wolę to niż użycie dodatkowej flagi, która ma zostać przetestowana w stanie najwyższego poziomu. Przerwa / powrót jest bardziej bezpośrednia; lepiej określa intencję niż ustawienie kolejnej zmiennej.
źródło
<
porównania muszą<=
pasować do rozwiązania OP„Zło” zależy od tego, jak z nich korzystasz. Zazwyczaj przerwy w konstrukcjach zapętlonych używam TYLKO wtedy, gdy pozwoli mi to zaoszczędzić cykle, których nie można zapisać przez refaktoryzację algorytmu. Na przykład przeglądanie kolekcji w poszukiwaniu elementu o wartości w określonej właściwości ustawionej na true. Jeśli wszystko, co musisz wiedzieć, to że jeden z elementów miał tę właściwość ustawioną na true, po osiągnięciu tego wyniku dobrze jest przerwać, aby odpowiednio zakończyć pętlę.
Jeśli użycie przerwy nie sprawi, że kod będzie łatwiejszy do odczytania, krótszy do uruchomienia lub zapisania cykli w przetwarzaniu w znaczący sposób, najlepiej ich nie używać. Zazwyczaj koduję do „najniższego wspólnego mianownika”, gdy jest to możliwe, aby upewnić się, że każdy, kto mnie śledzi, może z łatwością spojrzeć na mój kod i dowiedzieć się, co się dzieje (nie zawsze mi się to udaje). Przerwy zmniejszają to, ponieważ wprowadzają nieparzyste punkty wejścia / wyjścia. Nadużywane mogą zachowywać się bardzo podobnie jak nieudane oświadczenie „goto”.
źródło
Absolutnie nie ... Tak, użycie
goto
jest złe, ponieważ psuje strukturę twojego programu, a także bardzo trudno jest zrozumieć przepływ sterowania.Ale korzystanie z takich stwierdzeń jest
break
icontinue
jest absolutnie konieczne w dzisiejszych czasach i wcale nie jest uważane za złą praktykę programowania.A także niezbyt trudne do zrozumienia przepływu kontroli w użyciu
break
icontinue
. W konstrukcjach takich jakswitch
nabreak
rachunku jest to absolutnie konieczne.źródło
Zasadnicze pojęcie wynika z możliwości semantycznej analizy programu. Jeśli masz jeden wpis i jedno wyjście, matematyka potrzebna do oznaczenia możliwych stanów jest znacznie łatwiejsza niż w przypadku zarządzania ścieżkami rozwidlenia.
Po części ta trudność odzwierciedla koncepcyjne uzasadnienie twojego kodu.
Szczerze mówiąc, twój drugi kod nie jest oczywisty. Co to robi? Czy kontynuuje „kontynuuj”, czy „kontynuuje” pętlę? Nie mam pojęcia. Przynajmniej twój pierwszy przykład jest jasny.
źródło
Zamieniłbym twój drugi fragment kodu na
nie z powodu zwięzłości - tak naprawdę uważam, że jest to łatwiejsze do odczytania i dla kogoś, kto rozumie, co się dzieje. Ogólnie rzecz biorąc, warunki dla twoich pętli powinny być zawarte wyłącznie w tych warunkach pętli, które nie są zaśmiecone w całym ciele. Jednak istnieją pewne sytuacje, w których
break
icontinue
mogą pomóc czytelności.break
więcej niżcontinue
mógłbym dodać: Dźródło
foreach
pętli, ponieważ spowoduje to iterację każdego elementu w kolekcji.for
Pętli jest podobny w tym, że nie powinien mieć warunkowego punkt końcowy. Jeśli potrzebujesz warunkowego punktu końcowego, musisz użyćwhile
pętli.break
Moim zdaniem powinno być tylko jedno maksimum z pętli.Nie zgadzam się z twoim szefem. Są odpowiednie dla miejsc
break
icontinue
ma być używany. W rzeczywistości przyczyną wykonania i obsługi wyjątków we współczesnych językach programowania jest to, że nie można rozwiązać każdego problemu za pomocą juststructured techniques
.Na marginesie nie chcę tutaj rozpoczynać dyskusji religijnej, ale możesz zmienić strukturę kodu, aby był jeszcze bardziej czytelny:
Po drugiej stronie notatki
Osobiście nie podoba mi się stosowanie
( flag == true )
warunkowe, ponieważ jeśli zmienna jest już wartością logiczną, to wprowadzasz dodatkowe porównanie, które musi się zdarzyć, gdy wartość logiczna ma odpowiedź, której chcesz - chyba że jesteś pewien, że twój kompilator zoptymalizuj to dodatkowe porównanie.źródło
boolean
jest a co oznacza zwięzła / elegancka terminologia, to może lepiej zatrudnij mądrzejszych opiekunów ;-)Zgadzam się z twoim szefem. Są złe, ponieważ wytwarzają metody o dużej złożoności cyklomatycznej. Takie metody są trudne do odczytania i trudne do przetestowania. Na szczęście istnieje łatwe rozwiązanie. Wyodrębnij treść pętli do osobnej metody, w której „kontynuacja” staje się „powrotem”. „Powrót” jest lepszy, ponieważ po „powrocie” to koniec - nie ma obaw o stan lokalny.
Aby „przerwać”, wypakuj samą pętlę do osobnej metody, zastępując „przerwanie” słowem „powrót”.
Jeśli wyodrębnione metody wymagają dużej liczby argumentów, jest to wskazanie do wyodrębnienia klasy - albo zbierz je do obiektu kontekstu.
źródło
Myślę, że to problem tylko wtedy, gdy jest głęboko osadzony w wielu pętlach. Trudno wiedzieć, do której pętli się zrywasz. Kontynuacja może być również trudna, ale myślę, że prawdziwy ból pochodzi z przerw - logika może być trudna do naśladowania.
źródło
break 2
; dla innych wydaje się, że używane są tymczasowe flagi boolO ile nie są używane jako ukryte goto, jak w poniższym przykładzie:
Nic mi nie jest. (Przykład widoczny w kodzie produkcyjnym, meh)
źródło
Nie lubię żadnego z tych stylów. Oto co wolałbym:
Naprawdę nie lubię używać
return
do przerywania funkcji. To jest jak nadużyciereturn
.Za pomocą
break
również nie zawsze jest czytelne.Jeszcze lepiej może być:
mniej zagnieżdżania, a złożone warunki są przekształcane w zmienne (w prawdziwym programie trzeba by mieć lepsze nazwy, oczywiście ...)
(Cały powyższy kod jest pseudokodem)
źródło
break
icontinue
. Nienormalne zakończenie pętli jest po prostu dziwne i nie podoba mi się to.continue
oznacza pominięcie funkcji przejścia do następnej pętli; nie „kontynuuj egzekucję”Nie. To sposób na rozwiązanie problemu i istnieją inne sposoby jego rozwiązania.
Wiele obecnych języków głównego nurtu (Java, .NET (C # + VB), PHP, pisz własne) używa „break” i „kontynuuj”, aby pominąć pętle. Oba zdania „ustrukturyzowane goto (s)”.
Bez nich:
Z nimi:
Zauważ, że kod „przerwa” i „kontynuuj” jest krótszy i zwykle zamienia „a” w „zdania” na „for” lub „foreach”.
Oba przypadki są kwestią stylu kodowania. Wolę ich nie używać , ponieważ pełny styl pozwala mi mieć większą kontrolę nad kodem.
Właściwie pracuję w niektórych projektach, w których użycie tych zdań było obowiązkowe.
Niektórzy programiści mogą myśleć, że nie są to konieczne, ale hipotetyczne, gdybyśmy musieli je usunąć, musielibyśmy usunąć „while” i „do while” („powtarzaj do”, chłopaki pascal) ;-)
Wniosek, nawet jeśli wolę ich nie używać, uważam, że jest to opcja, a nie zła praktyka programistyczna.
źródło
Nie jestem przeciwko
continue
ibreak
zasady, ale myślę, że są one bardzo niskiego poziomu konstruktów, które bardzo często mogą być zastąpione przez coś jeszcze lepszego.Używam C # jako przykładu, rozważmy przypadek iteracji nad kolekcją, ale chcemy tylko elementów spełniających pewne predykaty i nie chcemy robić więcej niż maksymalnie 100 iteracji.
Wygląda to POWAŻNIE czysto. Nie jest to trudne do zrozumienia. Sądzę jednak, że wiele zyskałoby na byciu bardziej deklaratywnym. Porównaj to z następującymi:
Może połączenia Gdzie i Odbierz nie powinny nawet być w tej metodzie. Może to filtrowanie należy wykonać PRZED przekazaniem kolekcji do tej metody. W każdym razie, odchodząc od niskiego poziomu i skupiając się bardziej na rzeczywistej logice biznesowej, staje się bardziej jasne, czym faktycznie jesteśmy zainteresowani. Łatwiej jest podzielić nasz kod na spójne moduły, które bardziej odpowiadają dobrym praktykom projektowym i tak dalej na.
Niski poziom nadal będzie istniał w niektórych częściach kodu, ale chcemy to ukryć tak bardzo, jak to możliwe, ponieważ wymaga to energii mentalnej, którą moglibyśmy wykorzystać do uzasadnienia problemów biznesowych.
źródło
Code Complete ma fajną sekcję na temat używania
goto
i wielokrotnych zwrotów z procedury lub pętli.Ogólnie rzecz biorąc, nie jest to zła praktyka.
break
lubcontinue
powiedz dokładnie, co będzie dalej. I zgadzam się z tym.Steve McConnell (autor Code Complete) używa prawie takich samych przykładów, jak ty, aby pokazać zalety używania różnych
goto
instrukcji.Jednak nadużywanie
break
lubcontinue
może prowadzić do złożonego i niemożliwego do utrzymania oprogramowania.źródło