Natknąłem się na to pytanie przed chwilą i ściągam z tego trochę materiału: czy istnieje nazwa konstruktu „break n”?
Wydaje się to być niepotrzebnie złożonym sposobem na instruowanie programu, aby wyszedł z podwójnie zagnieżdżonej pętli for:
for (i = 0; i < 10; i++) {
bool broken = false;
for (j = 10; j > 0; j--) {
if (j == i) {
broken = true;
break;
}
}
if (broken)
break;
}
Wiem, że podręczniki mówią, że stwierdzenia goto to diabeł, a ja wcale ich nie lubię, ale czy to uzasadnia wyjątek od reguły?
Szukam odpowiedzi, które dotyczą n-zagnieżdżonych pętli.
UWAGA: Niezależnie od tego, czy odpowiesz tak, nie, czy gdzieś pomiędzy, odpowiedzi całkowicie bliskie nie są mile widziane. Zwłaszcza jeśli odpowiedź brzmi „nie”, należy podać uzasadniony powód (i tak nie jest to zbyt daleko od przepisów Stack Exchange).
coding-style
goto
Panzercrisis
źródło
źródło
for (j = 10; j > 0 && !broken; j--)
, to nie potrzebujeszbreak;
instrukcji. Jeśli przeniesiesz deklaracjębroken
do przed rozpoczęciem pętli zewnętrznej, to jeśli można to zapisać tak,for (i = 0; i < 10 && !broken; i++)
to myślę, że niebreak;
są konieczne.goto
: goto (C # Reference) - „Instrukcja goto jest również przydatna, aby wydostać się z głęboko zagnieżdżonych pętli”.goto
jest faktycznie wymagany w przełączniku C #, aby przejść do innej sprawy po wykonaniu dowolnego kodu w pierwszej sprawie. To jednak jedyny przypadek, w którym użyłem goto.Odpowiedzi:
Widoczna potrzeba wprowadzenia instrukcji wynika z tego, że wybrałeś słabe wyrażenia warunkowe dla pętli .
Stwierdzasz, że chciałeś, aby pętla zewnętrzna trwała tak długo,
i < 10
a wewnętrzna - tak długo, jakj > 0
.Ale w rzeczywistości nie było to, czego chciałeś , po prostu nie powiedziałeś pętlom o prawdziwym stanie, który chciałbyś, aby ocenili, a następnie chcesz go rozwiązać za pomocą break lub goto.
Jeśli powiesz pętlom swoje prawdziwe zamiary na początek , nie będziesz potrzebować przerw ani goto.
źródło
i
na końcu zewnętrznej pętli, więc zwarcie przyrostu jest ważne, aby kod wyglądał w 100% równoważnie. Nie twierdzę, że cokolwiek w twojej odpowiedzi jest złe, chciałem tylko zaznaczyć, że natychmiastowe przerwanie pętli było ważnym aspektem kodu.i
na końcu zewnętrznej pętli w pytaniu OP.goto
(jestem pewien, że są lepsze), nawet jeśli zrodziło się pytanie, które wydaje się używać go jako takiego, ale aby zilustrować podstawowe działaniebreak(n)
w językach, w których „ t wspierać to.W tym przypadku można przefakturować kod na osobną procedurę, a następnie bezpośrednio
return
z wewnętrznej pętli. To negowałoby potrzebę wyrwania się z zewnętrznej pętli:źródło
i
po zakończeniu zewnętrznej pętli. Aby naprawdę odzwierciedlić, żei
jest to ważna wartość, tutajreturn true;
ireturn false;
powinny być obiereturn i;
.Nie, nie uważam, że jest
goto
to konieczne. Jeśli napiszesz to w ten sposób:Posiadanie
&&!broken
obu pętli pozwala na wyrwanie się z obu pętli, kiedyi==j
.Dla mnie takie podejście jest lepsze. Warunki pętlowe są bardzo jasne:
i<10 && !broken
.Działającą wersję można zobaczyć tutaj: http://rextester.com/KFMH48414
Kod:
Wynik:
źródło
broken
w swoim pierwszym przykładzie w ogóle ? Po prostu użyj przerwy na wewnętrznej pętli. Ponadto, jeśli absolutnie musisz użyćbroken
, dlaczego deklarujesz to poza pętlami? Wystarczy zadeklarować je wewnątrz teji
pętli. Jeśli chodzi owhile
pętlę, nie używaj tego. Szczerze mówiąc, myślę, że udaje mu się być mniej czytelnym niż wersja goto.broken
ponieważ nie lubię korzystać zbreak
instrukcji (z wyjątkiem przypadku przełączania, ale o to chodzi). Jest zadeklarowany poza zewnętrzną pętlą, na wypadek, gdy chcesz przerwać WSZYSTKIE pętlei==j
. Masz rację, ogólnie rzecz biorąc, musiszbroken
tylko zadeklarować przed przerwaniem najbardziej zewnętrznej pętli.i==2
na końcu pętli. Warunek powodzenia powinien również zwierać przyrost, jeśli ma on być w 100% równoważny abreak 2
.broken = (j == i)
bardziej niż if i, jeśli pozwala na to język, umieszczając złamaną deklarację w inicjalizacji zewnętrznej pętli. Chociaż trudno powiedzieć te rzeczy w tym konkretnym przykładzie, ponieważ cała pętla nie działa (nic nie zwraca i nie ma skutków ubocznych), a jeśli coś robi, prawdopodobnie robi to wewnątrz if (chociaż nadal lubiębroken = (j ==i); if broken { something(); }
lepiej osobiście)i
po zakończeniu zewnętrznej pętli. Innymi słowy, jedyną naprawdę ważną myślą jest to, kiedy dokładnie wyłamuje się z zewnętrznej pętli.Krótka odpowiedź brzmi „to zależy”. Opowiadam się za wyborem dotyczącym czytelności i zrozumiałości twojego kodu. Sposób na goto może być bardziej czytelny niż poprawianie warunku lub odwrotnie. Możesz także wziąć pod uwagę zasadę najmniejszego zaskoczenia, wytyczne stosowane w sklepie / projekcie oraz spójność bazy kodu. Na przykład goto do opuszczenia przełącznika lub obsługi błędów jest powszechną praktyką w bazie kodu jądra Linux. Zauważ też, że niektórzy programiści mogą mieć problemy z odczytaniem twojego kodu (albo wychowany z mantrą „goto is evil”, albo po prostu nie nauczył się, ponieważ ich nauczyciel tak uważa), a niektórzy z nich mogą nawet „osądzić cię”.
Ogólnie rzecz biorąc, goto idące dalej w tej samej funkcji jest często dopuszczalne, szczególnie jeśli skok nie jest zbyt długi. Przypadek użycia pozostawienia zagnieżdżonej pętli jest jednym ze znanych przykładów goto „not so evil”.
źródło
W twoim przypadku goto wystarczy ulepszenia, aby wyglądało kusząco, ale nie jest to tak wiele ulepszenia, że warto złamać regułę, aby nie używać ich w bazie kodu.
Pomyśl o swoim przyszłym recenzencie: on / ona będzie musiał pomyśleć: „Czy ta osoba jest złym programistą, czy też jest to przypadek, w którym warto było skorzystać z goto? Pozwól mi poświęcić 5 minut na samodzielne podjęcie decyzji lub zbadanie Internet." Dlaczego, do licha, miałbyś to zrobić dla swojego przyszłego programisty, skoro nie możesz całkowicie użyć goto?
Zasadniczo ta mentalność dotyczy całego „złego” kodu, a nawet go definiuje: jeśli teraz działa i jest dobry w tym przypadku, ale dokłada starań, aby sprawdzić, czy jest poprawny, co w tej erze będzie zawsze robić, wtedy nie Zrób to.
Biorąc to pod uwagę, tak, stworzyłeś jedną z nieco bardziej ostrych spraw. Możesz:
Bardzo trudno jest mi stworzyć przypadek, w którym podwójna pętla nie jest tak spójna, że i tak nie powinna być własną rutyną.
Nawiasem mówiąc, najlepszą dyskusją na ten temat, jaką widziałem, jest Kod ukończony przez Steve'a McConnella . Jeśli lubisz krytycznie myśleć o tych problemach, zdecydowanie zalecamy lekturę, nawet od okładki do okładki, pomimo jej ogromu.
źródło
TLD; DR: Nie konkretnie, tak ogólnie
W tym konkretnym przykładzie, którego użyłeś, powiedziałbym „nie” z tych samych powodów, które wskazało wiele innych. Możesz łatwo refaktoryzować kod do równorzędnej dobrej formy, co eliminuje potrzebę
goto
.Ogólnie rzecz biorąc, zakazanie
goto
jest ogólnością opartą na zrozumiałej naturzegoto
i kosztach jej używania. Część inżynierii oprogramowania podejmuje decyzje wdrożeniowe. Uzasadnienie tych decyzji następuje poprzez porównanie kosztów z korzyściami, a gdy korzyści przewyższają koszty, wdrożenie jest uzasadnione. Kontrowersje powstają z powodu różnych wycen, zarówno kosztów, jak i korzyści.Chodzi o to, że zawsze będą wyjątki, w których
goto
uzasadnienie jest uzasadnione, ale tylko dla niektórych z powodu różnych wycen. Szkoda, że wielu inżynierów po prostu wyklucza techniki celowej ignorancji, ponieważ czytają je w Internecie, a nie zastanawiają się dlaczego.źródło
Nie sądzę, aby ten przypadek uzasadniał wyjątek, ponieważ można go zapisać jako:
źródło