Wątek przeniesiony 2022-07-22 11:04 z Edukacja przez cerrato.

Czemu używanie break jest złe?

0

Cześć.
Jakiś czas temu wykładowca z Algorytmów i struktur danych powiedział, że szanujący się programista nie użyje w pętli przerwania break. Stwierdził, że zawsze da się napisać kod tak, żeby obyło się bez breaka. Jestem w stanie w to uwierzyć, ale ciekawi mnie czemu taka niechęć i czy wy - doświadczeni programiści, słyszeliście coś na ten temat? Jest to jakaś niepisana zasada programistów?

Dodam, że facet jest w podeszłym wieku, ale wydawało się, że naprawdę zna się na rzeczy.

64

Wszystko zależy od kontekstu problemu. Gadanie, że break jest zawsze złe to zwyczajna bzdura.

15

Niestety to kłamstwo bardzo często powtarzane przez wykładowców, bo jedyne doświadczenie jakie mają to studia i książki.

Pomył wziął się stąd, że kiedyś nadużywano goto. Pojawił się ruch całkowicie przeciwstawny ruch (https://en.wikipedia.org/wiki/Structured_programming), który można skrócić do jeden return na końcu funkcji i tyle. Samo goto jest złe, ale takie przeciwieństwo również, bo czytelność na tym cierpi. Zamiast prostego return/break musimy wprowadzać sterujące zmienne bool (a im mniej zmiennych tym lepiej) a zamiast płaskich wcięć mamy drabinki ifów, które czasami są gorsze do rozkodowania niż demonizowane goto.

Już nie mówiąc, że taki styl kodowania czasami odbija się na wydajności np. model przenoszenia wartości w C++ za bardzo nie lubi jak się często przypisuje wartości do zmiennych zamiast je np. zwracać.
Poczytaj co o tym myślą niedoświadczeni programiści, którzy piszą kompilatory https://llvm.org/docs/CodingStandards.html#use-early-exits-and-continue-to-simplify-code

9

Stwierdził, że zawsze da się napisać kod tak, żeby obyło się bez breaka

Tak samo jak da się napisać aplikację w assemblerze albo w czystym WinAPI, ale pytanie - po co?
Nie uważam, żeby korzystanie z break było czymś złym.
Ważna jest inna kwestia - na ile osoba, która to robi, wie co robi oraz czy ma to wpływ na czytelność kodu.

Poszerzając ten ostatni argument - wydaje mi się, że w kwestii czytelności kodu, nawet takie siłowe unikanie break może mieć negatywny wpływ na czytelność. W sensie - zamiast prostej pętli i jej przerwania gdy trzeba, to (żeby zadowolić wykładowcę) zacznie się stosować jakieś dziwne wynalazki, ekwilibrystykę albo jakąś ifologię, żeby tylko na siłę nie wrzucić break. Znaczy - zamiast wstawić break tam, gdzie rozwiąże on problem, to będziesz pół godziny kombinować jak to zrobić inaczej i zamiast tych 5 liter napiszesz 10 linii, których jedynym sensownym efektem będzie zaciemnienie obrazu i nadmierne skomplikowanie.

Fajnie, jakbyś poprosił tego wykładowcę o kilka przykładów. Albo - sam daj mu parę konstrukcji, gdzie jeden prosty break załatwia sprawię i poproś o pokazanie, jak to przerobić. I się okaże, czy koleś jest cwaniakiem-teoretykiem, czy rzeczywiście osiągnął wyższy poziom oświecenia, a rozwiązanie jakie zaproponuje spowoduje, że nam wszystkim opadną szczęki do samej ziemi ;)

3

Dobrze dla Ciebie autorze, że jednak nie wierzysz we wszystko co się do Ciebie mówi i jednak czujesz potrzebę sprawdzenia pewnych rzeczy. Tak jak koledzy wcześniej napisali, wykładowcy często żyją w swoim abstrakcyjnym świecie z wymyślonymi problemami na które dzielnie wymyślają rozwiązania, które nikomu nie są potrzebne. Najlepiej jeszcze zrobić z tego publikację w dzienniku akademickim, którego nikt nie czyta. break i continue to narzędzia do rozwiązywania pewnych problemów a nie kłopot, z którym trzeba się uporać.

1

W pętli wydaje mi się, że break jest bardziej czytelne od robienia zmiennych sterujących, chociaż myślę, że to zależy od konkretnego przykładu. Dopiero wtedy można by rozpatrywać czytelność. Ale generalnie myślę, że to zależy od konkretnej sytuacji.

Jeszcze od kontekstu zależy - w switch/case naturalne jest wychodzenie break albo return, ale to nie pętla.

I od języka programowania, ale zakładam, że mówimy o językach ze składnią inspirowaną C. Chociaż... break nawet i w Pythonie jest, więc to chyba bardziej uniwersalny konstrukt.

AlanOGLL napisał(a):

szanujący się programista nie użyje w pętli przerwania break.

Stwierdzenie pachnące fanatyzmem. Nieważne, czy break jest "dobry" czy "zły", to co to za stwierdzenie w ogóle?

https://xkcd.com/378/

xD

Stwierdził, że zawsze da się napisać kod tak, żeby obyło się bez breaka.

Idąc tą logiką - zawsze da się napisać kod tak, żeby nie używać pętli for, bo zawsze da się ją emulować za pomocą while. Czy pętla for jest zła?

0

Jestem zdania, że należy z dużą dozą ostrożności podchodzić do tego, jak ktoś twierdzi, że czegoś "nigdy" nie należy stosować lub "zawsze" należy stosować. W pracy też się spotykam z takimi sytuacjami, jak np. ktoś twierdzi "nigdy nie używać singletona, bo to antywzorzec", więc tu nie wylewałbym wiadra pomyj na wykładowców, bo i na uczelni, i w pracy, i w innych miejscach można spotkać ludzi ze swoimi odchyłami.

1
AlanOGLL napisał(a):

Jakiś czas temu wykładowca z Algorytmów i struktur danych powiedział, że szanujący się programista nie użyje w pętli przerwania break. Stwierdził, że zawsze da się napisać kod tak, żeby obyło się bez breaka.

No da się napisać.

Mógłbyś wziać dowolny element każdego języka programowania, i powiedzieć "da się napisać program nie używając feature'a x". Mógłbyś napisać program bez klas, polimorfizmu, tablic, etc. Więc argument że "się da", nic sam w sobie nie znaczy.

Co do samego pytania, żeby nie użyć break w pętli, a nie chcesz wykonać operacji na większej ilości elementów, to musiałbyś użyć innego sposobu ograniczenia iteracji, co albo się sprowadza do ifowania, albo nie używania w ogóle pętli, tylko np rekurencji albo skoków, albo jeszcze innego poziomu abstrakcji na kolekcje.

Nie widzę nic złego w break, continue ani innych control-statementsach.

3

Czasami może się przydać, ale ja to nawet nie pamiętam, kiedy pętli ostatnio używałem, a co dopiero break.

0

Niby chodzi o to, że jak przerywasz pętlę, to kod jest mniej czytelny. Podobnie jak masz więcej niż jeden return w metodzie. Jeżeli ten kawałek kodu ma kilkaset linii, to faktycznie tak jest. Tylko pisanie takich tasiemców jest większym złem niż ten break, czy inny return.

Swoją drogą, zastanawiam się jak ten szanujący się programista rozwiązałby np. problem typu "pobierz plik z internetu, jak się nie uda, to zaczekaj 5 sekund i spróbuj jeszcze raz, jeżeli nie uda się n razy, to daj sobie spokój".

Ale może ja mam błędne podejście, bo programowaniem zarabiam na życie, więc szanowanie się jest luksusem, na który mnie nie stać.

@somekind: Nie używasz pętli w znaczeniu for, while, do while, czy w znaczeniu "nie wykonuję wielokrotnie tego samego ciągu instrukcji" (czyli nie używasz wszelkiej maści forEach i innych takich)?

4
Kanciarzek napisał(a):

Jestem zdania, że należy z dużą dozą ostrożności podchodzić do tego, jak ktoś twierdzi, że czegoś "nigdy" nie należy stosować lub "zawsze" należy stosować. W pracy też się spotykam z takimi sytuacjami, jak np. ktoś twierdzi "nigdy nie używać singletona, bo to antywzorzec", więc tu nie wylewałbym wiadra pomyj na wykładowców, bo i na uczelni, i w pracy, i w innych miejscach można spotkać ludzi ze swoimi odchyłami.

Z tymi antywzorcami to trzeba rozróżnić dyskurs od praktyki.

Pewne rzeczy należy publicznie krytykować, bo są niepotrzebne, nadużywane i generujące problemy. Inni programiści niekoniecznie mogą sobie zdawać z tego sprawę, że "X considered harmful". I będą je wciskać wszędzie.

Co nie znaczy, że tego X nie można użyć, jeśli rozwiązuje to jakiś problem albo jeśli wiemy, jaki jest koszt użycia i zaciągamy świadomie koszt technologiczny. Pytanie jednak, czy używamy X świadomi jego wad.

Podobnie jak masz więcej niż jeden return w metodzie.

Zdania są podzielone. Ja jednak jestem zwolennikiem early return. Bo to się lepiej czyta i kod jest bardziej liniowy, nie trzeba robić dodatkowego zagnieżdżenia kodu.

1
LukeJL napisał(a):

Z tymi antywzorcami to trzeba rozróżnić dyskurs od praktyki.

Pewne rzeczy należy publicznie krytykować, bo są niepotrzebne, nadużywane i generujące problemy. Inni programiści niekoniecznie mogą sobie zdawać z tego sprawę, że "X considered harmful". I będą je wciskać wszędzie.

Co nie znaczy, że tego X nie można użyć, jeśli rozwiązuje to jakiś problem albo jeśli wiemy, jaki jest koszt użycia i zaciągamy świadomie koszt technologiczny. Pytanie jednak, czy używamy X świadomi jego wad.

Ale czym innym jest krytykować i wskazywać jakie dana rzecz może sprawiać problemy, a czym innym jest tworzyć prawa w postaci "Nigdy nie należy używać X". Tak, jak napisałeś, może zaistnieć sytuacja, że zdecydujemy się na użycie danego elementu, który może sprawiać gdzieniegdzie problem, ale być tu spoko rozwiązaniem. W sytuacji, gdy tworzymy sobie takie prawa, to się ograniczamy. Wydaje się, że lepszym rozwiązaniem jest już stwierdzenie "Należy unikać X", ale bez jakiegoś uzasadnienia i wyjaśnienia jest prawie tyle samo warte (no to kiedy jest dozwolone? jak nie wiadomo, jaki jest powód, to już lepiej nie używać, dla zasady)

Tu akurat podałem przykład tego singletona, bo sam się z takim stwierdzeniem spotykałem. Chodziło mi o tworzenie sobie radykalnych praw z uzasadnieniem przez autorytet ("bo wykładowca tak powiedział / bo senior tak powiedział / bo na mądrym portalu tak napisano" itp.) i jak najbardziej pochwalam zachowanie autora, że próbuje dociekać czy to prawda i dlaczego. Sam się łapałem na tym, że niektóre rzeczy robiłem, bo ktoś mi tak powiedział, że się robi, a jak się zacząłem zastanawiać dlaczego i dopytywać, to rzeczywistość okazywała się nie taka czarno-biała. Ba, nawet pod wpływem analizy może się okazać, że dana zasada jest bez sensu (jak najwidoczniej jest z "Nie używać breaków"). I może miałem jakieś szczęście, ale więcej fanboyów różnych rozwiązań spotykam w pracy niż miałem okazję poznać wykładowców z radykalnymi twierdzeniami (chociaż może wykładowca-fanatyk wskaźników i C++ robił za cały wydział stwierdzeniami "dopiero po poznaniu typu wskaźnikowego możecie myśleć o byciu programistami", czy "koderzy Javy czy Pythona nie mogą nazywać się programistami, ponieważ nie używają wskaźników", ale to było takie jaskrawe, że nie dało się traktować tego poważnie xD)

3

@somekind: Nie używasz pętli w znaczeniu for, while, do while, czy w znaczeniu "nie wykonuję wielokrotnie tego samego ciągu instrukcji" (czyli nie używasz wszelkiej maści forEach i innych takich)?

Domyślam się o klasycznego fora.. @somekind zapewne używa LINQ, Javovcy Streamów itp

4
piotrpo napisał(a):

Swoją drogą, zastanawiam się jak ten szanujący się programista rozwiązałby np. problem typu "pobierz plik z internetu, jak się nie uda, to zaczekaj 5 sekund i spróbuj jeszcze raz, jeżeli nie uda się n razy, to daj sobie spokój".

Mam nadzieję, że nie pętlą.

return Policy
  .Handle<WebException>()
  .WaitAndRetry(new[]
  {
    TimeSpan.FromSeconds(5),
    TimeSpan.FromSeconds(10),
    TimeSpan.FromSeconds(15)
  }, (ex, timeSpan, retryCount, context) =>
  {
    _logger.Error(ex, $"Error - try retry (count: {retryCount}, timeSpan: {timeSpan})");
  })
  .Execute(() => webClient.DownloadString(url));
}

@somekind: Nie używasz pętli w znaczeniu for, while, do while, czy w znaczeniu "nie wykonuję wielokrotnie tego samego ciągu instrukcji" (czyli nie używasz wszelkiej maści forEach i innych takich)?

Tak jak @scibi_92 - technologie pozwalające na deklaratywne filtrowanie pozwalają pominąć używanie pętli. A jak już masz prawidłowo odfiltrowane dane do przetworzenia, to iterujesz po wszystkich, nie trzeba break.

1
AlanOGLL napisał(a):

Cześć.
Jakiś czas temu wykładowca z Algorytmów i struktur danych powiedział, że szanujący się programista nie użyje w pętli przerwania break.
Stwierdził, że zawsze da się napisać kod tak, żeby obyło się bez breaka.

Do treści prezentowanych przez wykładowców polskich uczelni podchodziłbym z przymrużeniem oka :-P

Jestem w stanie w to uwierzyć, ale ciekawi mnie czemu taka niechęć i czy wy - doświadczeni programiści, słyszeliście coś na ten temat?

Niechęć do break się zdarza wśród mniej doświadczonych. Do goto i return też.

Taka trochę nadgorliwość programistycznych neofitów.

Niektórym nawet tak zostaje, nie wyłączając seniorów i principali. Tyle, że "złej baletnicy" przeszkadza nawet break w switchu.

Zupełnie inną kwestią są przypadki nadużywania tych instrukcji, ale to też wynika z braku umiejętności.

Jest to jakaś niepisana zasada programistów?

Nie, to przedkładanie źle pojmowanego puryzmu nad racjonalnością i praktyką produkcji oprogramowania.

Potrzeba użycia instrukcji break (a nawet goto) często wynika z celu, który chcemy osiągnąć.

Wówczas najczęściej nic nie stoi na przeszkodzie, aby wysokiej jakości kod wykorzystywał te instrukcje.

Dodam, że facet jest w podeszłym wieku, ale wydawało się, że naprawdę zna się na rzeczy.

Zgaduję, że kluczem jest wydawało się.

Z własnego doświadczenia (5 lat na WEiTI PW) wiem, że czasem jak wykładowca coś palnie, to trzeba 3 razy się dopytywać, żeby uwierzyć, że profesor, doktor (re)habilitowany czy inny docent z milionem literek przed nazwiskiem mógł coś takiego powiedzieć.

8

Wolę mieć break albo return w funkcji w zamian za mała ilość wcięć i kod, który czyta się z góry do dołu bez zbędnych skomplikowanych drabinek instrukcji sterujących.

Mam też trochę doświadczenia w programowaniu low level w C i parę goto na sumieniu, bo taki był standard kodowania w projekcie. Zakaz wczesnych powrotów, miał być tylko jeden return, za to goto dozwolone, ale tylko podczas skakania do wyjścia z funkcji (które robi dodatkowo jakieś czyszczenie) lub do wyjścia obsługującego błąd. W cpp mamy RAII i return by posprzątał, ale w c już nie, stąd taka specyficzna obsługa błędów. Kto kodował w niskopoziomowo pewnie zrozumie o czym mówię.

Czemu to mówię? Bo nie uważam, by w tym kontekście nawet i goto był zły.

Tak samo jest z break. Jestem w stanie sobie wyobrazić sytuację gdzie owa instrukcja zaciemnia kod, a nawet sprawia, że jest trudny do utrzymania. Ale to nie znaczy, że w ogóle nie należy jej używać.

0
nalik napisał(a):

Wolę mieć break albo return w funkcji w zamian za mała ilość wcięć i kod, który czyta się z góry do dołu bez zbędnych skomplikowanych drabinek instrukcji sterujących.

Mam też trochę doświadczenia w programowaniu low level w C i parę goto na sumieniu, bo taki był standard kodowania w projekcie. Zakaz wczesnych powrotów, miał być tylko jeden return, za to goto dozwolone, ale tylko podczas skakania do wyjścia z funkcji (które robi dodatkowo jakieś czyszczenie) lub do wyjścia obsługującego błąd. W cpp mamy RAII i return by posprzątał, ale w c już nie, stąd taka specyficzna obsługa błędów. Kto kodował w niskopoziomowo pewnie zrozumie o czym mówię.

Czemu to mówię? Bo nie uważam, by w tym kontekście nawet i goto był zły.

Bo nie było złe :)

Tylko, że jak ktoś nie umie w programowanie, to nie potrzeba goto, aby zrobił sobie krzywdę. Równie dobrze można sobie strzelić w stopę nieumiejętnie posługując się wskaźnikami, typami danych, alokacjami pamięci, format stringami i czymkolwiek innym.

Co do typów danych, to miałem jako słuchacz wykładu kontakt z "technicznie najlepszym" wykładowcą uczelni z pierwszej trójki rankingu "Perspektywy", który np. twierdził, że ntohl i htonl służą do konwersji longów, a ntohs i htons do konwersji shortów - zgaduję zatem, że koledze @AlanOGLL mógł trafić się podobny aparat w roli wykładowcy, tyle że akurat padło na bogu ducha winną instrukcję break.

Po prostu jak się nie ma kontaktu z rzeczywistym kodem, to się nie ma pojęcia o zasadach jego konstrukcji, bo skąd miałaby ta wiedza pochodzić? Z podręczników akademickich?

A jak się ma kilka niepopartych komercyjną praktyką tytułów przed nazwiskiem, to łatwo można wpaść w pułapkę samouwielbienia i przekonania o swojej wyższości, nie zdając sobie sprawy z faktów i jednocześnie nie będąc w stanie zrobić nawet jednego taska na poziomie mida.

0

Zwykle zwrócenie True działa jak break i kończy mapowanie, przeszukiwanie.

[1,2,3,4,5].some((el)=>{ console.log(el); return el==3})
[*map(lambda x: print(x) if x < 4 else True, a)]
10

Wszystko zależy od kontekstu.
Jak się uczysz programowania, robisz skrypty, proste programiki itp. to największym problemem jest to żeby program powstał. I wtedy break, goto itp. nie są niczym specjalnie złym.

Jak pracujesz 20-40 godzin tygodniowo jako programista, ileś lat, i robisz oprogramowanie, gdzie błąd oznacza tragedię, bo komuś odrzuci transakcję płatniczą w Cocomo i zniszczy wieczór, jednak największym problemem jest nie żeby jakoś zadziałało, ale żeby nie wywalała się produkcja i żeby jak przyjdzie jakiś losowy junior coś poprawić w Twoim kodzie, to nie mógł łatwo zepsuć.

Dlatego nakładamy sobie kajdany i nie używamy konstrukcji, które są błędogenne. NIe tylko nie używamy, ale nawet mamy systemy, które sprawdzają kod i odrzucają jeśli pojawi się zakazana konstrukcja (lintery).

W moim przypadku oznacza to, że break w kodzie produkcyjnym to użyłem ostatnio pewnie 25 lat temu :-) Pętli też nie napisałem już od wielu lat.
A to nawet nie są największe ograniczenia. Ba, w mojej firmie używa się dużo języka, który nie jest turing complete (Dhall), tylko po to, żeby nie było się łatwo pomylić.

Ale jeszcze raz - zależy od kontekstu, doświadczenia, rodzaju oprogramowania jakie się pisze (co się stanie w przypadku błędu), zespołu i języka programowania.
Uważam, że bardzo dobrze, że wykładowca takie rzeczy mówi, bo to skłania do dyskusji. I jak widać jest zupełnie życiowy.
Sam pamiętam jak w latach 90tych usłyszałem od nauczyciela, że goto to zło i od razu przyjąłem, że to właśnie akademickie pierdoły, bo przecież bez goto żyć się nie da.

1 użytkowników online, w tym zalogowanych: 0, gości: 1