break jest złem?

0

Break jest podobno zły, bo utrudnia coś z niezmiennikami.

Czy faktycznie zamiast wyskakiwać z pętli brejkiem powinienem zmieniać wartość licznika, albo kombinować z wyskakiwaniem przy pomocy wyjątków(throw null)?
Wydaje mi się to dziwne, ale wolę zapytać.

2
DużaTajemnicaWiedzy napisał(a):

Break jest podobno zły, bo utrudnia coś z niezmiennikami.

Czy faktycznie zamiast wyskakiwać z pętli brejkiem powinienem zmieniać wartość licznika, albo kombinować z wyskakiwaniem przy pomocy wyjątków(throw null)?
Wydaje mi się to dziwne, ale wolę zapytać.

Powiem to tak - niezmienników użyłem raz w życiu, i to dla własnej satysfakcji. Breaków używam codziennie.

Nie, używanie breaków to dobry pomysł. A kombinowanie z licznikiem i, o zgrozo, wyjątki - to zły pomysł.

PS. Ech, ci językowi puryści. Najpierw 'goto jest złe', później 'wiele returnów jest złe' teraz 'break jest zły'. Niedługo będzie można używać tylko wywołań funkcji :>.

2

goto jest ok ale w switchu i pod warunkiem ze sie go uzywa w naprawde wyjatkowych sytuacjach...

@msm Twój post ma wiekszy sens kiedy czyta się go od ostatniego akapitu ;P

1

Dla przykładu w Scali:

  • continue i goto w ogóle nie ma,
  • break jest emulowany za pomocą wyjątków, więc raczej się go unika,
  • returnów też jest bardzo mało, ponieważ domyślnie wynikiem funkcji jest wartość ostatniego wyrażenia przed opuszczeniem funkcji, return w ogóle wygląda dość obco w kodzie Scalowym,

Niestety ani język Java nie wspiera TCO (tail call optimization) ani nie robi tego HotSpot, więc zastępowanie dziwolągów rekurencją (w Javie) zaowocuje zmniejszoną wydajnością i przepełnieniami stosu.

Czasem piszę w Javie takie koślawe kody:

while (true) {
  rób A;
  if (cośtam)
    break;
  rób B;
}

W Scali można by spokojnie zrobić z tego rekurencję ogonową:

def m {
  rób A
  if (cośtam) {
    rób B
    m()
  }
}
m()

Przepływ sterowania jest łatwo dostrzegalny w wersji z rekurencją dzięki wcięciom w kodzie, a wersja z breakiem jest nieczytelna jeżeli rób A i rób B to wiele linijek kodu - break się po prostu gubi przy przelatywaniu kodu wzrokiem.

.NET ma TCO w standardzie, więc tam unikanie koślawego kodu z breakami, continue itp powinno być łatwe. Chociaż pewnie i tak prawie nikt tego nie unika.

1

Dzięki
Doczytałem o niezmiennikach i wychodzi na to, że break jest zły tylko jak chcę matematycznie sprawdzić algorytm, a irl gdzie testng załatwia sprawę poprawności, break w niczym nie przeszkadza.
Dobrze to rozumiem?

0

Nie do końca. Break jak każda instrukcja skoku przeszkadza trochę w analizie przepływu sterowania w kodzie (i musisz ślęczeć z debuggerem zamiast rzucić okiem). Oczywiście jeśli piszesz jak normalny człowiek, metody po max 20 linijek to problemu nie ma. Ale jak tworzysz jakieś kobyły (a konieczność stosowania break może na to wskazywać) to może być nieciekawie.
Przekręcanie licznika / rzucanie wyjątkiem zamiast break to jakiś chory pomysł i absolutnie nie należy tak robić. Można po prostu warunki w pętlach zmienić, ale czasem bardziej to zaciemnia niż pomaga.

3

Break jest podobno zły, bo utrudnia coś z niezmiennikami.

Niedługo usłyszymy że if jest zły, bo utrudnia określenie, czy dany kod się wykonuje czy nie.

0

break to tylko goto bez jawnej etykiety, w wielu wypadkach można napisać lepszy kod bez break niż z jego użyciem. Oczywiście są języki, jak Python, gdzie break w pewnych sytuacjach jest niezbędny.

0

Dobra, to na prostym przykładzie sprawdzenia czynie dany znak nie powtarza się za często w haśle.

public void check(boolean type, String input, String login, Set<PassValidationErrors> results) {
        char prev=0;
        int repetitions=0;
        for(int i=0;i<input.length();i++){
            if(input.charAt(i)==prev){
                repetitions++;
                if(repetitions>MAX_REPETITIONS){
                    results.add(PassValidationErrors.to_many_same_chars_in_a_row);
                    break;
                }
            }else{
                prev=input.charAt(i);
                repetitions=0;
            }
        }
    }

Czy jest sens przekształcać ten kod na taki bez berak?

0

Po coś ten break jest w języku, jeśli by go nie było, radziłbyś sobie inaczej. Moim zdaniem we wskazanym przypadku w niczym to nie przeszkadza, zostawiłbym jak jest i poszedł dalej.

0
noone_logged_out napisał(a):

Po coś ten break jest w języku, jeśli by go nie było, radziłbyś sobie inaczej. Moim zdaniem we wskazanym przypadku w niczym to nie przeszkadza, zostawiłbym jak jest i poszedł dalej.

Chociaż jak patrzę na ten kod, to bym korzystał raczej z jakichś foreach/while() niż for(), a wtedy break byłby niepotrzebny.

1

Akurat w podanym przykładzie cały kod można zapisać znacznie bardziej elegancko.

public void check(String input, Set<String> results) {
		char prev = 0;
		int repetitions = 0;
		int length = input.length();
		for (int i = 0; i < length; i++) {
			if (input.charAt(i) == prev) {
				repetitions++;
			} else {
				prev = input.charAt(i);
				repetitions = 0;
			}
			if (repetitions > MAX_REPETITIONS) {
				results.add("to_many_same_chars_in_a_row");
				return;
			}
		}
	}

Logika kodu mówi nam, że w momencie gdy jest za dużo taki samych znaków po kolei należy zgłosić błąd i przerwać działanie pętli. Jednocześnie przerwanie działania pętli oznacza zakończenie metody i wymaga od osoby śledzącej kod do skakania. W takim przypadku return znacznie lepiej oddaje to co chciał osiągnąć programista.

0

Zawsze można to napisać w Haskellu, tam nie ma breaków więc nie da się ich użyć:

check = or . (zipWith (==) <$> init <*> tail)

albo, trochę mniej wtf-owo:

check x = or $ zipWith (==) (init x) (tail x)
0

Faktycznie return wydaje się w tym przypadku lepszy, ale za to układ ifów wolę swój, bo sprawdzanie czy liczba powtórzeń została przekroczona odbywa się rzadziej(chociaż wiem, że wielkiej różnicy to nie robi, bo nikt nie będzie sobie ustawiał hasła na 1M znaków).

Dzięki za odpowiedzi.
To jeszcze ostatnie pytanie w temacie i już was nie męczę.
Czy możecie podać jakiś przykładowy kod w javie gdzie break w pętli kompletnie nie pasuje i trzeba go zastąpić czymś innym?

0

@msm nie...

@autor generalnie uciekanie z pętli przez break to głupie rozwiązanie. Szczególnie w Javie. Jednak "najciekawszy" przypadek jaki dane mi było spotkać to:

while(wxBrnid.fetchAll())
    break;

tylko po to by wykonać kod w fetchAll.

0

Wersja rekurencyjna w Javie:

    private void check2(String input, Set<String> results, char prev, int repetitions, int i) {
        if (repetitions > MAX_REPETITIONS)
            results.add("to_many_same_chars_in_a_row");
        else if (i < input.length())
            check2(input, results, input.charAt(i), input.charAt(i) == prev ? repetitions + 1 : 0, i + 1);
    }
	
    public void check1(String input, Set<String> results) {
        check2(input, results, input.charAt(0), 0, 1);
    }

W Scali byłoby mniej więcej coś takiego:

def check(input: String, results: Set[String]) {
  def check_(prev: Char, repetitions: Int, i: Int) {
    if (repetitions > MAX)
      results.add("max reached")
    else if (i < input.length)
      check_(input.charAt(i), if (input.charAt(i) == prev) repetitions + 1 else 0, i + 1)
  }
  check_(input.charAt(0), 0, 1)
}

Brak return, continue, break, for, while i goto w obu wersjach.

0

Rozumiem, że break można by zastąpić rzuceniem wyjątku, ale IMO skok do etykiety jest mniej "czasochłonny" a zmiana zmiennej sterującej pętlą to już dla mnie prawdziwe WTF - po pierwsze nieczytelne po drugie nie jest tożsame z break bo pętla i tak wykona się do momentu sprawdzenia warunku a może iść dalej i zastąpić continue czymś innym?

Jak już jesteśmy przy temacie to może czepię się jeszcze break przy switch'u.
Czy nie uważacie, że w językach o składni C-podobnej domyślnie powinno być break w przejściu do case(bez pisania tego słowa kluczowego)? Chyba w 90% przypadków i tak trzeba tego break'a wstawić po każdej lini a zamiast tego dodać słowo kluczowe skip czy goto gdy trafimy na pozostałe 10% przypadków - IMO o wiele lepiej jest to rozwiązane w pascalu.

0
msm napisał(a):

Zawsze można to napisać w Haskellu, tam nie ma breaków więc nie da się ich użyć:

Leniwa ewaluacja jest sama w sobie breakiem.

0

O'RLY? - Wibowit X minut temu

Przykład:

quicksort [] = []
quicksort (s:xs) = quicksort [x|x <- xs,x < s] ++ [s] ++ quicksort [x|x <- xs,x >= s] 

i teraz
minimum = head . quicksort
Sortowanie zostanie przerwane w momencie gdy będzie znany najmniejszy element.

0

Zauważ że jest to konstrukcja czysto funkcyjna, tzn operuje na niemutowalnych danych i to czy ewaluacja jest leniwa czy nie nie ma żadnego wpływu na wynik. Dopóki operujemy na danych skończonych rozmiarów, leniwość nie wpływa w ogóle na sposób rozumowania. Tymczasem break, return (oczywiście poza przypadkiem gdy jest ostatnią instrukcją w metodzie), continue czy goto w językach (częściowo) imperatywnych zaciemnia przepływ sterowania.

0
Wibowit napisał(a):

Tymczasem break, return (oczywiście poza przypadkiem gdy jest ostatnią instrukcją w metodzie), continue czy goto w językach (częściowo) imperatywnych zaciemnia przepływ sterowania.

Mnie nie interesują języki imperatywne. Odnosiłem się do stwierdzenia, iż w haskellu nie ma breaków, albo czegoś podobnego.
Podobieństwo jakie ja widzę jest w parafrazie działania tej instrukcji: wstrzymuje dalsze obliczenia w pętli.
Podobieństwo może i naciągane ale to zawsze dobry powód do flejma.

Peace.

0
Leniwiec napisał(a):

Odnosiłem się do stwierdzenia, iż w haskellu nie ma breaków, albo czegoś podobnego.

No a Cont to pies? Kontynuacje użyte wewnątrz dowolnej iteracji to funkcyjna forma goto, to break/return/continue z etykietą w formie argumentu.

0
PS napisał(a):

No a Cont to pies? Kontynuacje użyte wewnątrz dowolnej iteracji to funkcyjna forma goto, to break/return/continue z etykietą w formie argumentu.

Do monad jeszcze nie doszedłem. Haskell idzie mi jak krew z nosa. Wykorzystałem to co dotychczas wiedziałem.

0
Leniwiec napisał(a):
Wibowit napisał(a):

Tymczasem break, return (oczywiście poza przypadkiem gdy jest ostatnią instrukcją w metodzie), continue czy goto w językach (częściowo) imperatywnych zaciemnia przepływ sterowania.

Mnie nie interesują języki imperatywne. Odnosiłem się do stwierdzenia, iż w haskellu nie ma breaków, albo czegoś podobnego.
Podobieństwo jakie ja widzę jest w parafrazie działania tej instrukcji: wstrzymuje dalsze obliczenia w pętli.
Podobieństwo może i naciągane ale to zawsze dobry powód do flejma.

Peace.

  1. Break wcale nie musi przerywać obliczeń (w szczególności nie musi być używane do osiągnięcia leniwości obliczeń), służy tylko do zmiany przepływu sterowania.
  2. Twórcy Haskella nawet nie muszą znać takiej instrukcji jak break i jej sposobu działania, żeby zaimplementować leniwą ewaluację. Break, goto, continue czy return zmieniający przepływ sterowania mogą być zastąpione zwykłym ifem.
  3. JVM stosuje szereg optymalizacji powodujący, iż nie zawsze wykonuje cały kod jak leci.

Ogólnie chciałem przekazać, że porównanie leniwej ewaluacji do breaka jest w zasadzie bez sensu.

0
Wibowit napisał(a):

Ogólnie chciałem przekazać, że porównanie leniwej ewaluacji do breaka jest w zasadzie bez sensu.

Nie breaka, a idei breaka.

0

Ideą breaka jest szybkie wyjście z pętli i tylko tyle. Najważniejszym celem takiej ucieczki z pętli jest ominięcie instrukcji zmieniających stan w dalszej części pętli.

0
Wibowit napisał(a):

Ideą breaka jest szybkie wyjście z pętli i tylko tyle. Najważniejszym celem takiej ucieczki z pętli jest ominięcie instrukcji zmieniających stan w dalszej części pętli.

Wobec tego przejdzmy do mniej funkcyjnego języka wspierającego leniwość - pythona.

MAX_REPETITIONS = 3

def count_char_in_row(iter):    # z zamysłem napisane tak imperatywnie, bez pomocy itertools
    prev = None
    rep  = 0                 # definiuję to jako liczbę dotychczasowych liter, bo Koziołek ma niespójność
    for c in iter:
        if c == prev:
            rep = rep + 1    # mutacja stanu                 -- jest
        else:
            rep, prev = 1, c
        yield(rep)           # ucieczka przed dalszą mutacją -- jest

def check(input, results):
    if MAX_REPETITIONS in count_char_in_row(input):
        print "Too many same chars in a row"  # albo results.add("Too many ...")
    else:
        print "OK"
0

break'a nie lubię. Bo nie. Wydaje mi się nieelegancki.
Nie lubię też komendy return; w metodach typu void.
Zazwyczaj breaka obchodzę dodaniem booleana:
np.

boolean run = true;
while(run){
// costam
if(condition){
run = false;
}
}

Nie wiem czemu ale mam wrodzoną niechęć do continue, break czy też wspomnianego "pustego" return. Ba, samych wielokrotnych return staram się unikać. Co ciekawe, nie wziąłem tego z żadnej książki, z żadnego szkolenia ani też z żadnej strony. Trudno mi powiedzieć skąd mi się to wzięło - tym bardziej, że zaczynałem pisać w Basic'u, gdzie funkcje typu goto były normą.

0

Akurat zastępowanie breaka ifem i flagą to żadna poprawa, a raczej jeszcze gorsze rozwiązanie. Elegancka jest rekurencja ogonowa, wtedy nie masz pętli, skoków (oprócz ifa), flag sterujących itd Od razu widać przepływ sterowania, niezmienniki, nie ma mutowania stanu w kółko, itd

PS:
Napiszę jeszcze raz, bo ktoś może wejść i nie czytać całości tematu: ani HotSpot (referencyjny kompilator JIT bajtkodu Javowego) ani javac (standardowy kompilator języka Java) nie wspierają TCO (optymalizacji pętli ogonowych). Dlatego stosowanie rekurencji ogonowej w Javie może spowodować wybuch objawiający się wyjątkiem StackOverflowException. Kompilator Scali zamienia rekurencję ogonową funkcji samej w siebie na zwykłą pętlę (rekurencja ogonowa z przekazaniem sterowania do innej funkcji nie jest optymalizowana), dzięki czemu w tak prostych przypadkach można stosować tail-calle.

0

Zdawało mi się, że piszemy o Javie jako takiej?
Czasami kod po prostu wymaga wielu pętli, a definiowanie dodatkowych tysiąca i jednej metody to moim zdaniem przesada. O ile w Scali rzeczywiście takie coś może mieć sens, o tyle w Javie w jakiś sposób trzeba uciec z pętli. Ja breaków nie lubię, więc używam flag - i nie wydaje mi się, żeby to było mocno nieczytelne.
W ogóle w Javie brakuje pewnych rzeczy, co zresztą nie tylko ja zauważyłem. No, ale to przecież temat na nieco inną dyskusję.

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