goto jest prawie powszechnie odradzane. Czy warto używać tego stwierdzenia?

Komentarze

  • xkcd.com/292
  • goto jest tym, co ostatecznie robi procesor, ale nie działa dobrze z tym, co ludzie muszą zrozumieć i wyodrębnić, ponieważ nie ma znaku na celu koniec. Porównaj z Intercal COMEFROM.
  • @Thorbj ø rnRavnAndersen, co jeszcze ludzie mają na myśli, gdy ' mówisz o przejściach stanów w maszynie stanów? Czy ' czy widzisz podobieństwo? ” Idź do danego stanu „, który ' co to znaczy, a wszystko inne byłoby niczym innym jak niepotrzebną komplikacją tak trywialnej rzeczy. A co masz na myśli przez ” brak znaku „? Etykieta jest takim znakiem.
  • @ SK-logic, zależy od tego, z jakiej odległości pozwolisz, aby goto ' s nadeszło. Maszyny stanowe nie wymagają goto do implementacji.
  • @Thorbj ø rnRavnAndersen, oczywiście goto nie jest wymagane. Jest to po prostu najbardziej racjonalny sposób implementacji ich w językach imperatywnych, ponieważ jest najbliższy samej semantyce przejścia stanów. A jego miejsce docelowe może znajdować się dość daleko od źródła, dlatego ' nie utrudnia czytelności. Moim ulubionym przykładem takiego kodu jest D.E. Knuth ' s implementację gry przygodowej.

Odpowiedź

Zostało to omówione kilka razy w Stack Overflow, a Chris Gillum podsumował możliwe zastosowania goto :

Czyste wyjście z funkcji

Często w funkcji możesz przydzielić zasoby i wyjść w wielu miejscach. Programiści mogą uprościć swój kod, umieszczając kod czyszczenia zasobów na końcu funkcji, wszystkie ” punkty wyjścia ” funkcji będą gotowe etykieta czyszczenia. W ten sposób nie musisz pisać kodu czyszczącego w każdym ” punkcie wyjścia ” funkcji.

Wychodzenie z zagnieżdżonych pętli

Jeśli jesteś w zagnieżdżonej pętli i musisz wyrwać się z wszystkie pętle, goto może uczynić to dużo czystszym i prostszym niż instrukcje break i if-check.

Niskopoziomowe ulepszenia wydajności

Jest to poprawne tylko w kodzie krytycznym dla perf, ale instrukcje goto są wykonywane bardzo szybko i mogą dać ci impuls podczas poruszania się po funkcji. Jest to jednak miecz obosieczny, ponieważ kompilator zazwyczaj nie może zoptymalizować kodu zawierającego gotos.

Spierałbym się, jak wielu innych twierdzi , że we wszystkich tych przypadkach użycie goto jest używane jako sposób na wydostanie się z zakodowanego kąta, do którego się zakodowano, i jest ogólnie objawem kodu, który może zostać zrefaktorowany .

Komentarze

  • Pierwszy problem został bardzo starannie rozwiązany przez finally bloki we współczesnych językach oraz drugi rozwiązuje oznaczony break s. Jeśli ' utkniesz z C, jednak goto to właściwie jedyny sposób na eleganckie rozwiązanie tych problemów.
  • Bardzo dobre uwagi. Podoba mi się sposób, w jaki Java umożliwia wyłamywanie się z wielu poziomów pętli.
  • @Chinmay – w końcu tylko blokuje mają zastosowanie do 1) języków współczesnych, które je mają oraz b) można tolerować narzut (obsługa wyjątków ma narzut. ) Oznacza to, że użycie finally jest ważne tylko w tych warunkach. Są bardzo rzadkie, ale ważne sytuacje, w których goto jest najlepszym rozwiązaniem.
  • Dobra ludzie … jaka jest różnica między break 3 a break myLabel i goto myLabel. Och, to ' to właściwe tylko słowo kluczowe. Jeśli używasz break, continue lub innego podobnego słowa kluczowego, w rzeczywistości używasz goto (Jeśli używasz for, while, foreach, do/loop, używasz warunkowego goto.)
  • Informacje o wydajności na niskim poziomie – zachowanie nowoczesnego procesora może być trudne do przewidzenia (potok, egzekucja poza kolejnością), więc prawdopodobnie w 99% przypadków nie jest to tego warte lub nawet może być wolniejsze. @MatthewWhited: Scoping. Jeśli wprowadzisz zakres w dowolnym miejscu, nie jest jasne (dla człowieka), jakie konstruktory należy nazywać (problem istnieje również w C).

Odpowiedź

Konstrukcje przepływu sterowania wyższego poziomu zwykle odpowiadają pojęciom z dziedziny problemowej. Decyzja if / else to decyzja oparta na jakimś warunku. Pętla nakazuje wielokrotne wykonywanie pewnych czynności. Nawet instrukcja przerwania mówi „robiliśmy to wielokrotnie, ale teraz musimy przestać”.

Z drugiej strony instrukcja goto zwykle odpowiada koncepcji w uruchomionym programie, a nie w problematyczna domena. Mówi, aby kontynuować wykonywanie w określonym punkcie w programie . Ktoś czytający kod musi wywnioskować , co to oznacza w odniesieniu do domeny problemowej.

Oczywiście wszystkie konstrukcje wyższego poziomu można zdefiniować w kategoriach goto i prostych gałęzi warunkowych . To nie oznacza, że są po prostu przebrani. Pomyśl o nich jako o ograniczonych gotach – i to właśnie ograniczenia sprawiają, że są użyteczne. Instrukcja break jest implementowana jako skok na koniec otaczającej pętli, ale lepiej o niej myśleć jako działając na pętli jako całości.

Jeśli wszystko inne jest równe, kod, którego struktura odzwierciedla strukturę domeny problemowej, jest zwykle łatwiejszy do odczytania i utrzymania.

Nie ma przypadków, w których instrukcja goto jest absolutnie wymagana (istnieje twierdzenie na ten temat), ale są przypadki, w których może być najmniej złym rozwiązaniem. Te przypadki różnią się od język do języka, w zależności od tego, jakie konstrukcje wyższego poziomu obsługuje język.

Na przykład w C uważam, że istnieją trzy podstawowe scenariusze, w których goto jest odpowiednie.

  1. Wyłamywanie się z zagnieżdżonej pętli. Byłoby to niepotrzebne, gdyby język miał instrukcję break z etykietą.
  2. Wyskakiwanie z fragmentu kodu (zazwyczaj treści funkcji) w przypadku błędu lub innego nieoczekiwanego zdarzenia . Byłoby to niepotrzebne, gdyby język miał wyjątki.
  3. Implementacja jawnej maszyny skończonej. W tym przypadku (i myślę, że tylko w tym przypadku) goto odpowiada bezpośrednio pojęciu w domenie problemowej, przechodząc z jednego stanu do określonego innego stanu, w którym stan bieżący jest reprezentowany przez aktualnie wykonywany blok kodu .

Z drugiej strony, jawna maszyna skończona może być również implementowana z instrukcją switch wewnątrz pętli. Ma to tę zaletę, że każdy stan zaczyna się w tym samym miejscu w kodzie, co może być przydatne na przykład do debugowania.

Główne zastosowanie goto w dość nowoczesnym języku (takim, który obsługuje if / else i pętle) ma na celu zasymulowanie konstrukcji przepływu sterowania, której brakuje w języku.

Komentarze

  • Jak dotąd jest to najlepsza odpowiedź – raczej zaskakujące, jak na pytanie, które ma półtora roku. 🙂
  • +1 do ” najlepszej jak dotąd odpowiedzi „. — ” Głównym zastosowaniem goto w dość nowoczesnym języku (takim, który obsługuje if / else i pętle) jest symulacja przepływu sterowania konstrukcja, której ' brakuje w języku. ”
  • W maszynie stanu instrukcji przełączania każdy zapis do wartość kontrolna to w rzeczywistości goto. SSSM ' to często dobre struktury do użycia, ponieważ paralizują stan SM z konstrukcji wykonawczej, ale nie ' nie eliminują w rzeczywistości ” gotos „.
  • ” Ponieważ wszystko inne jest równe, kod, którego struktura odzwierciedla strukturę domeny problemowej, jest zwykle łatwiejszy do odczytania i utrzymania. ” Ja ' wiedziałem o tym od dawna, ale brakowało mi słów, aby tak precyzyjnie przekazać to w sposób, programiści mogą i faktycznie to zrozumieją. Dziękuję za to.
  • ” twierdzenie o programie strukturalnym ” bawi mnie, ponieważ zwykle jasno pokazuje Zwróć uwagę, że używanie strukturalnych instrukcji programowania do emulacji goto jest znacznie mniej czytelne niż zwykłe użycie goto!

Answer

Z pewnością zależy to od języka programowania. Głównym powodem, dla którego goto było kontrowersyjne, są jego złe skutki, które pojawiają się, gdy kompilator pozwala ci używać go zbyt swobodnie. Mogą pojawić się problemy, na przykład, jeśli pozwoli ci to użyć goto w taki sposób, że możesz teraz uzyskać dostęp do niezainicjowanej zmiennej lub gorzej, aby przejść do innej metody i zepsuć wywołanie stos. Kompilator powinien być odpowiedzialny za zablokowanie bezsensownego przepływu sterowania.

Java próbowała „rozwiązać” ten problem, całkowicie uniemożliwiając goto. Jednak Java pozwala na użycie return wewnątrz bloku finally, co powoduje nieumyślne połknięcie wyjątku.Ten sam problem nadal istnieje: kompilator nie wykonuje swojej pracy. Usunięcie goto z języka nie rozwiązało tego problemu.

W języku C # goto jest tak samo bezpieczne jak break, continue, try/catch/finally i return. Nie pozwala ci używać niezainicjalizowanych zmiennych, nie pozwala ci wyskoczyć z końcowego bloku itp. Kompilator będzie narzekał. Dzieje się tak, ponieważ rozwiązuje rzeczywisty problem, który jest taki, jak powiedziałem, bezsensowny przepływ sterowania. goto nie anuluje w magiczny sposób analizy przypisania określonego i innych rozsądnych sprawdzeń kompilatora.

Komentarze

  • Chodzi ci o to, że C # ' s goto nie jest złe? Jeśli tak, dzieje się tak w przypadku każdego rutynowo używanego języka współczesnego z konstrukcją goto, a nie tylko C # …

Odpowiedź

Tak. Gdy twoje pętle są zagnieżdżone na kilka poziomów, goto jest jedynym sposobem na eleganckie wyrwanie się z wewnętrznej pętli. Inną opcją jest ustawienie flagi i przerwanie każdej pętli, jeśli ta flaga spełnia warunek. To jest naprawdę brzydkie i dość podatne na błędy. W takich przypadkach goto jest po prostu lepsze.

Oczywiście instrukcja Java oznaczona break robi to samo rzecz, ale bez umożliwienia przeskoczenia do dowolnego punktu w kodzie, co starannie rozwiązuje problem, nie dopuszczając elementów, które powodują, że goto jest zły.

Komentarze

  • goto nigdy nie jest elegancką odpowiedzią. Kiedy twoje pętle są zagnieżdżone na kilku poziomach, twój problem polega na tym, że nie udało się zaprojektować kodu w celu uniknięcia pętli multinest. wroga. (Przeczytaj artykuły ” Lambda: The Ultimate … „.) Użycie goto do wyrwania się z zagnieżdżonych pętli głęboki poziom to założenie bandaża na otwarte wielokrotne pęknięcie: może to być proste, ale nie jest ' właściwą odpowiedzią.
  • I oczywiście nigdy nie jest dziwnym przypadkiem, gdy potrzebne są głęboko zagnieżdżone pętle? Bycie dobrym programistą nie jest nie chodzi tylko o przestrzeganie zasad, ale także o to, aby wiedzieć, kiedy je łamać.
  • @ JohnR.Strohm Nigdy nie mów nigdy. Jeśli używasz w programowaniu terminów takich jak ” nigdy „, to ' najwyraźniej nie masz do czynienia z przypadkami narożnymi, optymalizacją, ograniczeniami zasobów itp. W głęboko zagnieżdżonych pętlach goto jest rzeczywiście najczystszym i najbardziej eleganckim sposobem na wyjście z pętli. Czy ' nie ma znaczenia, jak bardzo ' zrefaktorowałeś swój kod.
  • @ JohnR.Strohm: Drugi najczęściej brakujący funkcja przy przełączaniu z VB.NET do C #: pętla Do. Dlaczego? Ponieważ mogę zmienić jedną pętlę, która wymaga głębokiej przerwy, w Do pętlę i wpisać Exit Do i oto ' s no GoTo. Zagnieżdżone pętle występują cały czas. Zerwanie stosów zdarza się w niektórych% przypadków.
  • @ JohnR.Strohm Jedynym powodem, dla którego mówisz ” goto, nigdy nie jest elegancka odpowiedź ” wynika z tego, że zdecydowałeś, że goto nigdy nie jest elegancką odpowiedzią, a zatem żadne rozwiązanie używające goto nie może być elegancką odpowiedzią.

Odpowiedź

Większość zniechęcenia pochodzi z pewnego rodzaju „religii”, która została stworzona przez Boga Djikstrę, która we wczesnych latach 60. była nie do zniesienia. :

  • wskocz do dowolnego bloku kodu
    • funkcja niewykonana od początku
    • pętle niewykonane od początku
    • pominięto inicjalizację zmiennej
  • odskoczyć od dowolnego bloku kodu bez żadnego możliwego czyszczenia.

To nie ma nic wspólnego z goto oświadczenie współczesnych języków, których istnienie jest spowodowane jedynie wsparciem cr Tworzenie struktur kodu innych niż te podane w języku.

W szczególności pierwszy główny punkt powyżej jest już dozwolony, a drugi jest czyszczony (jeśli goto z blok, stos jest rozwijany prawidłowo i wywoływane są wszystkie właściwe destruktory)

Możesz odwołać się do tej odpowiedzi , aby dowiedzieć się, jak nawet kod użycie goto może być nieczytelne. Problem nie polega na samym goto, ale w złym użyciu.

Mogę napisać cały program bez użycia if, po prostu for. Oczywiście nie będzie dobrze czytelny, będzie wyglądał niezgrabnie i niepotrzebnie skomplikowany.

Ale problem nie dotyczy for. To ja.

Na przykład break, continue, throw, bool needed=true; while(needed) {...} itd. zauważają więcej niż maskaradę goto, aby uciec przed sejmitarami zelotów Djikstrarian, że – 50 lat po wynalezieniu nowoczesnych języków – nadal chcą swoich więźniów. Zapomnieli, o czym mówił Djikstra, pamiętają tylko tytuł jego notatki (GOTO uważany za szkodliwy, a nie był to nawet jego jedyny tytuł: został zmieniony przez redaktora) i obwinianie i bash, bash i obwinianie każdego konstruktora posiadającego te 4 litera umieszczona w kolejności.

Jest rok 2011: czas zrozumieć, że goto ma do czynienia z GOTO oświadczenie, o którym Djikstra był przekonujący.

Komentarze

  • ” Na przykład przerwa, kontynuacja , rzut, bool potrzebne = prawda; podczas gdy (potrzebne) {…} itp. zauważają coś więcej niż maskaradę, ale ” Nie ' nie zgadzam się z tym. Wolałbym, żeby ” takie rzeczy jak przerwa itp. Były ograniczone goto ” lub coś w tym rodzaju. Główny problem z goto polega na tym, że zazwyczaj jest zbyt potężny. W zakresie, w jakim obecne goto jest mniej wydajne niż Dijkstra ' s, twoja odpowiedź jest dla mnie w porządku.
  • @MuhammadAlkarouri: Całkowicie się zgadzam. Właśnie znalazłeś lepsze sformułowanie, aby dokładnie wyrazić moją koncepcję. Chodzi mi o to, że czasami te ograniczenia mogą nie mieć zastosowania, a rodzaj ograniczenia, którego potrzebujesz, nie jest w języku. W takim razie bardziej ” potężniejsze „, mniej wyspecjalizowane, jest tym, co umożliwia obejście tego problemu. Na przykład Java break n nie jest dostępna w C ++, stąd goto escape, nawet jeśli escape nie musi wychodzić z pętli n-ple.

Odpowiedź

Dziwne goto tutaj lub tam, o ile jest lokalny dla funkcji, rzadko znacząco szkodzi czytelności. Często przynosi to korzyści, zwracając uwagę na fakt, że w tym kodzie dzieje się coś niezwykłego, co wymaga użycia dość nietypowej struktury kontrolnej .

Jeśli (lokalne) goto znacznie szkodzą czytelności, to zazwyczaj jest to znak, że funkcja zawierająca goto stała się zbyt złożona.

Ostatnie goto, które umieściłem w fragment kodu C polegał na zbudowaniu pary blokujących się pętli. Nie mieści się to w normalnej definicji „akceptowalnego” gotowego użycia, ale w rezultacie funkcja stała się znacznie mniejsza i wyraźniejsza. Aby uniknąć goto, wymagałoby szczególnie niechlujnego naruszenia DRY.

Komentarze

  • Odpowiedź na to pytanie jest najbardziej treściwie zawarta w starym ” Lay ' s Chipsy ziemniaczane ” slogan: ” Założę się, że możesz ' zjeść tylko jedną „. W Twoim przykładzie masz dwie ” zazębiające się ” (cokolwiek to znaczy, a ja ' m obstawiam, że to nie ' t ładne) pętle, a goto dał ci tanio wyjście. A co z konserwatorem, który musi zmodyfikować ten kod po potrąceniu przez autobus? Czy goto uczyni jego życie łatwiejszym czy trudniejszym? Czy te dwie pętle wymagają ponownego przemyślenia?

Odpowiedź

Myślę, że cała ta kwestia dotyczy szczekania niewłaściwe drzewo.

GOTO jako takie nie wydaje mi się problematyczne, ale raczej bardzo często jest objawem rzeczywistego grzechu: kod spaghetti.

Jeśli GOTO powoduje większe skrzyżowanie linii sterowania przepływem, to jest złe, kropka. Jeśli nie przekracza linii sterowania przepływem, jest nieszkodliwe. W szarej strefie pomiędzy mamy takie rzeczy jak bailouty pętli, wciąż istnieją języki, które nie dodały konstrukcji obejmujących wszystkie legalne szare przypadki.

Jedyny przypadek, w którym faktycznie go używam od wielu lat jest przypadek pętli, w której punkt decyzyjny znajduje się w środku pętli. Zostajesz ze zduplikowanym kodem, flagą lub GOTO. Uważam, że rozwiązanie GOTO jest najlepsze z trzech. Nie ma tutaj skrzyżowania linii kontroli przepływu, jest nieszkodliwe.

Komentarze

  • Przypadek, do którego nawiązujesz, jest czasami nazywany „półtora pętli” lub „N plus pół pętli” i jest to „słynny przypadek użycia dla goto, które wskakują do pętli (jeśli język na to pozwala; ponieważ w drugim przypadku wyskoczenie z pętli w środku jest zwykle trywialne bez goto ).
  • (Niestety, większość języków zabrania skakania do pętli.)
  • @KonradRudolph: Jeśli zaimplementujesz ” pętlę ” z GOTO nie ma innej struktury pętli, do której można by wskoczyć.
  • Myślę o goto jako o krzyku TUTAJ PRZEPŁYW KONTROLNY NIE ' T DOPASUJ NORMALNE PROGRAMOWANIE STRUKTURALNE WZORY . Ponieważ przepływy sterowania, które pasują do wzorców programowania strukturalnego, są łatwiejsze do rozważenia niż te, które nie ' t, należy próbować używać takich wzorców, gdy jest to możliwe; jeśli kod pasuje do takich wzorców, nie należy ' krzyczeć, że nie ' t. Z drugiej strony, w przypadkach, gdy kod naprawdę ' nie pasuje do takich wzorców, krzyczenie o tym może być lepsze niż udawanie, że kod pasuje, gdy naprawdę nie ' t.
  • @supercat Wyobraź sobie, że sam napisałeś harmonogram, a potem wiatr wyrywa ci go z rąk. Zgodnie z Twoją logiką nie powinieneś ' go pobierać, ponieważ pogoń za harmonogramem nie była ' t w Twoim harmonogramie.

Odpowiedź

Tak, goto można wykorzystać, aby zwiększyć doświadczenie programisty: http://adamjonrichardson.com/2012/02/06/long-live-the-goto-statement/

Jednak tak jak w przypadku każdego potężnego narzędzia (wskaźniki, wielokrotne dziedziczenie itp.), należy być zdyscyplinowanym Użyj tego. Przykład podany w odsyłaczu wykorzystuje PHP, które ogranicza użycie konstrukcji goto do tej samej funkcji / metody i wyłącza możliwość przejścia do nowego bloku sterującego (np. Pętla, instrukcja przełączania itp.)

Odpowiedź

Zależy od języka. Na przykład jest nadal szeroko stosowany w programowaniu Cobol. Pracowałem również nad urządzeniem Barionet 50, którego język programowania oprogramowania układowego jest wczesnym dialektem BASIC i oczywiście wymaga użycia Goto.

Odpowiedź

goto może być przydatne przy przenoszeniu starszego kodu asemblera do C. W pierwszej kolejności instrukcja: konwersja instrukcji do języka C, przy użyciu goto jako zamiennika instrukcji asemblera „s branch, może umożliwić bardzo szybkie przenoszenie.

Komentarze

  • Prawidłowo, ale jednym z argumentów przeciwko goto s jest to, że udaremniają one optymalizacje kompilatora.
  • @Sapphire_Brick: Wiele optymalizacji kompilatora jest zaprojektowanych wokół pewnych wzorców wykonywania. Jeśli zadanie do wykonania nie ' nie pasuje do obsługiwanych wzorców wykonywania, ” udaremnienie ” optymalizacji może nie być złą rzeczą.

Odpowiedź

Nie. Zawsze należy je zastąpić narzędziem bardziej specyficznym dla problemu, który goto jest używany do rozwiązania (chyba że nie jest dostępne w Twoim języku). Na przykład instrukcje przerwania i wyjątki rozwiązują wcześniej rozwiązane problemy związane z ucieczką pętli i obsługa błędów.

Komentarze

  • I ' nie twierdzę, że jeśli języki mają oba tych funkcji, goto jest zwykle nieobecny w tych językach.
  • Ale dzieje się tak, ponieważ nie ' nie potrzebują ich lub ponieważ ludzie uważają, że nie ' ich potrzebują?
  • Twoje poglądy są bigoteryjne. Jest wiele przypadków, gdy goto jest najbliższą semantyce domeny, w której występuje problem, cokolwiek innego byłoby brudnym hackem.
  • @ SK-logic: nie ' t myślę, że słowo ” bigoted ” oznacza to, co myślisz, że oznacza.
  • @Keith,

nietolerancyjnie oddany własnym opiniom i uprzedzeniom ” – to ' jest tym, co ja konsekwentnie obserwuj tutaj, z tym gotowym losem z zawiązanymi oczami. ” Powinny zawsze być zastępowane ” są fanatyczne ' s przynajmniej słowa. Nie ma nic bliskiego właściwej, rozsądnej opinii, a po prostu czysta fanatyzm. To ” zawsze ” nie pozostawia miejsca na żadną alternatywną opinię, rozumiesz?

Odpowiedź

Powiedziałbym, że nie. Jeśli uznasz za konieczne użycie GOTO, założę się, że trzeba będzie przeprojektować kod.

Komentarze

  • Może, ale ty Haven ' nie przedstawił żadnego uzasadnienia
  • Dijkstra przedstawił szczegółowe uzasadnienie dziesiątki lat temu.
  • Just Dogma. Żadnych rezonansów. Uwaga: Djikstra NIE JEST JUŻ WAŻNYM REZONEM: odnosił się do instrukcji BASIC lub FORTRAN GOTO z wczesnych ' lat 60. Nie mają one nic wspólnego z naprawdę anemicznymi (w porównaniu z potęgą tych) dzisiejszych potworów.
  • @EmilioGaravaglia Cóż, część jego rozumowania, która nadal ma zastosowanie do dziś, jest taka, że ' bardzo łatwo jest napisać kod spaghetti, korzystając z wielu gotowych rozwiązań. Jednak zgodnie z tą logiką nie powinniśmy ' używać wskaźników, ponieważ użycie tylu wskaźników, co number of gotos needed to create problems, spowodowałoby podobny chaos.

Dodaj komentarz

Twój adres email nie zostanie opublikowany. Pola, których wypełnienie jest wymagane, są oznaczone symbolem *