Vale la pena usare goto?


29

gotoè quasi universalmente scoraggiato. Usare questa affermazione è mai utile?



1
gotoè ciò che la cpu alla fine fa, ma non funziona bene con ciò che gli umani devono comprendere e astrarre a causa della mancanza di segni sull'estremità target. Confronta con Intercal COMEFROM.

2
@ ThorbjørnRavnAndersen, cos'altro significano gli umani quando parlano di transizioni di stato in una macchina statale? Non vedi una somiglianza? "Vai a un dato stato ", questo è ciò che significa e qualsiasi altra cosa non sarebbe altro che una complicazione inutile di una cosa così banale. E cosa intendi con "nessun segno"? L'etichetta è un tale segno.
Logica SK

@ SK-logic, dipende da quanto lontano consentirai di arrivare ai goto. Le macchine a stati non richiedono goto per l'implementazione.

@ ThorbjørnRavnAndersen, ovviamente gotonon è richiesto. È solo il modo più razionale di implementarli nelle lingue imperative, poiché è la cosa più vicina alla semantica stessa della transizione di stato. E la sua destinazione può essere abbastanza lontana dalla fonte, non ostacolerà la leggibilità. Il mio esempio preferito di tale codice è l'implementazione di DE Knuth del gioco Adventure.
SK-logic

Risposte:


50

Questo è stato discusso più volte su Stack Overflow e Chris Gillum ha riassunto i possibili usi digoto :

Uscire in modo pulito da una funzione

Spesso in una funzione, è possibile allocare risorse e dover uscire in più punti. I programmatori possono semplificare il loro codice inserendo il codice di pulizia delle risorse alla fine della funzione, tutti i "punti di uscita" della funzione dovrebbero andare sull'etichetta di pulizia. In questo modo, non è necessario scrivere il codice di pulizia in ogni "punto di uscita" della funzione.

Uscita da loop nidificati

Se sei in un ciclo nidificato e hai bisogno di uscire da tutti i loop, un goto può renderlo molto più pulito e più semplice delle dichiarazioni di interruzione e degli if-check.

Miglioramenti delle prestazioni di basso livello

Questo è valido solo nel codice perf-critico, ma le istruzioni goto vengono eseguite molto rapidamente e possono darti una spinta quando ti muovi attraverso una funzione. Questa è un'arma a doppio taglio, perché un compilatore in genere non può ottimizzare il codice che contiene gotos.

Direi, come molti altri affermerebbero, che in tutti questi casi, l'uso di gotoè usato come mezzo per uscire da un angolo in cui si è codificati, ed è generalmente un sintomo di codice che potrebbe essere riformulato.


28
Il primo problema è risolto in modo molto ordinato da finallyblocchi in linguaggi moderni, e il secondo è risolto da breaks. Se sei bloccato con C, tuttavia, gotoè praticamente l'unico modo per risolvere questi problemi con eleganza.
Chinmay Kanchi,

2
Punti molto buoni. Mi piace come Java permetta di uscire da più livelli di loop
Casebash,

4
@Chinmay - finalmente i blocchi si applicano solo a 1) lingue moderne che li hanno eb) puoi tollerare il sovraccarico (la gestione delle eccezioni ha un sovraccarico). Cioè, l'uso finallyè valido solo in quelle condizioni. Ci sono situazioni molto rare, ma valide, in cui un goto è la strada da percorrere.
luis.espinal,

21
Va bene gente ... che diamine è la differenza tra break 3o break myLabele goto myLabel. Oh, è proprio la parola chiave. Se stai usando break, continueo qualche parola chiave simile, infatti stai usando un goto(Se stai usando for, while, foreach, do/loopstai usando un goto condizionale.)
Matthew Whited

4
Informazioni sulle prestazioni di basso livello: il comportamento del moderno processore potrebbe essere difficile da prevedere (pipeline, esecuzione fuori ordine), quindi probabilmente nel 99% dei casi non ne vale la pena o addirittura potrebbe essere più lento. @MatthewWhited: Scoping. Se si entra nel campo di applicazione in un punto arbitrario, non è chiaro (per l'uomo) quali costruttori dovrebbero essere chiamati (il problema esiste anche in C).
Maciej Piechotka,

42

I costrutti di flusso di controllo di livello superiore tendono a corrispondere ai concetti nel dominio del problema. Un if / else è una decisione basata su una condizione. Un loop dice di eseguire alcune azioni ripetutamente. Anche una dichiarazione di rottura dice "lo stavamo facendo ripetutamente, ma ora dobbiamo fermarci".

Un'istruzione goto, d'altra parte, tende a corrispondere a un concetto nel programma in esecuzione, non nel dominio del problema. Dice di continuare l'esecuzione in un punto specificato nel programma . Qualcuno che legge il codice deve dedurre cosa significa rispetto al dominio problematico.

Naturalmente tutti i costrutti di livello superiore possono essere definiti in termini di goto e semplici rami condizionali. Ciò non significa che siano semplicemente goto sotto mentite spoglie. Pensa a loro come goto limitate - ed è le restrizioni che le rendono utili. Un'istruzione break è implementata come un salto alla fine del ciclo che la racchiude, ma è meglio pensarla come operante sul ciclo nel suo insieme.

A parità di tutto il resto, il codice la cui struttura riflette quella del dominio problematico tende a essere più facile da leggere e mantenere.

Non ci sono casi in cui un'istruzione goto è assolutamente necessaria (c'è un teorema in tal senso), ma ci sono casi in cui può essere la soluzione meno negativa. Tali casi variano da una lingua all'altra, a seconda di quali costrutti di livello superiore siano supportati dalla lingua.

In C, ad esempio, credo che ci siano tre scenari di base in cui un goto è appropriato.

  1. Uscire da un ciclo annidato. Ciò non sarebbe necessario se la lingua avesse un'istruzione break etichettata.
  2. Salvataggio da un tratto di codice (in genere un corpo di funzione) in caso di errore o altro evento imprevisto. Ciò non sarebbe necessario se la lingua avesse eccezioni.
  3. Implementazione di una macchina a stati finiti esplicita. In questo caso (e, penso, solo in questo caso) un goto corrisponde direttamente a un concetto nel dominio del problema, passando da uno stato a un altro stato specificato, in cui lo stato corrente è rappresentato da quale blocco di codice è attualmente in esecuzione .

D'altra parte, una macchina a stati finiti esplicita può anche essere implementata con un'istruzione switch all'interno di un ciclo. Ciò ha il vantaggio che ogni stato inizia nello stesso punto del codice, che può essere utile per il debug, ad esempio.

L'uso principale di un goto in un linguaggio ragionevolmente moderno (uno che supporta if / else e loop) è di simulare un costrutto del flusso di controllo che manca al linguaggio.


5
Questa è in realtà la risposta migliore finora - piuttosto sorprendente, per una domanda che ha un anno e mezzo. :-)
Konrad Rudolph,

1
+1 alla "migliore risposta finora". --- "L'uso principale di un goto in un linguaggio ragionevolmente moderno (uno che supporta if / else e loop) è di simulare un costrutto del flusso di controllo che manca al linguaggio."
Dave Dopson,

Nella macchina a stati dell'istruzione switch, ogni scrittura sul valore di controllo è in realtà a goto. Gli SSSM sono spesso buone strutture da usare, poiché separano lo stato SM dal costrutto dell'esecuzione, ma in realtà non eliminano i "goto".
supercat

2
"A parità di altre condizioni, il codice la cui struttura riflette quella del dominio problematico tende a essere più facile da leggere e mantenere." Lo conosco completamente da molto tempo, ma mancavano le parole per comunicarlo in modo così preciso in modo che altri programmatori possano e possano effettivamente capire. Grazie per questo.
Wildcard il

Il "teorema del programma strutturato" mi diverte, poiché tende a dimostrare chiaramente che l'uso di istruzioni di programmazione strutturate per emulare un goto è molto meno leggibile rispetto al semplice utilizzo di un goto!

11

Sicuramente dipende dal linguaggio di programmazione. Il motivo principale gotoè stato controverso a causa dei suoi effetti negativi che sorgono quando il compilatore ti consente di usarlo troppo liberamente. Ad esempio, possono sorgere problemi se ti consente di utilizzare gotoin modo tale da poter ora accedere a una variabile non inizializzata, o peggio ancora, per passare a un altro metodo e pasticciare con lo stack di chiamate. Dovrebbe essere responsabilità del compilatore impedire il flusso di controllo senza senso.

Java ha tentato di "risolvere" questo problema impedendo del gototutto. Tuttavia, Java ti consente di utilizzare returnall'interno di un finallyblocco e quindi causare un'ingiunzione inavvertitamente. Lo stesso problema è ancora presente: il compilatore non sta facendo il suo lavoro. La rimozione gotodalla lingua non l'ha riparata.

In C #, gotoè sicuro come break, continue, try/catch/finallye return. Non ti consente di utilizzare variabili non inizializzate, non ti consente di saltare da un blocco finalmente, ecc. Il compilatore si lamenterà. Questo perché risolve il vero problema, che è come ho detto flusso di controllo senza senso. gotonon annulla magicamente l'analisi di assegnazione definita e altri controlli del compilatore ragionevoli.


Il tuo punto è che C # gotonon è male? Se è così, questo è il caso di ogni linguaggio moderno usato di routine con un gotocostrutto, non solo C # ...
lvella

9

Sì. Quando i tuoi loop sono nidificati a diversi livelli di profondità, gotoè l' unico modo per uscire elegantemente da un loop interno. L'altra opzione è quella di impostare un flag ed uscire da ciascun loop se quel flag soddisfa una condizione. Questo è davvero brutto e piuttosto soggetto a errori. In questi casi, gotoè semplicemente meglio.

Naturalmente, la breakdichiarazione etichettata di Java fa la stessa cosa, ma senza permetterti di saltare a un punto arbitrario nel codice, che risolve il problema in modo ordinato senza consentire le cose che fanno del gotomale.


Qualcuno ha cura di spiegare il downvote? Questa era una risposta perfettamente valida alla domanda ...
Chinmay Kanchi,

4
goto non è mai una risposta elegante. Quando i loop sono nidificati a diversi livelli di profondità, il problema è che non si è riusciti a progettare il codice per evitare i loop multestestati. Le chiamate di procedura NON sono nemiche. (Leggi i documenti "Lambda: The Ultimate ...".) Usare un goto per uscire da anelli nidificati a diversi livelli di profondità sta mettendo un cerotto su una frattura multipla aperta: può essere semplice ma non è giusto risposta.
John R. Strohm,

6
E, naturalmente, non c'è mai il caso strano quando sono richiesti loop profondamente annidati? Essere un buon programmatore non è solo seguire le regole, ma anche sapere quando infrangerle.
Chinmay Kanchi,

2
@ JohnR.Strohm Mai dire mai. Se usi termini come "mai" nella programmazione, chiaramente non hai a che fare con casi angolari, ottimizzazione, contrapposizioni di risorse, ecc. Nei loop profondamente annidati, goto è davvero il modo più pulito ed elegante per uscire dai loop. Non importa quanto hai refactored il tuo codice.
Sujay Phadke,

@ JohnR.Strohm: seconda caratteristica più mancata nel passaggio da VB.NET a C #: il Doloop. Perché? Perché posso cambiare un loop che ha bisogno di una profonda interruzione in un Doloop e digitare Exit Doe non c'è GoTo. I cicli nidificati si verificano sempre. Rompere le pile succede un po 'del tempo.
Giosuè il

7

La maggior parte dello scoraggiamento deriva da una sorta di "religione" che è stata creata suscitando Dio Djikstra che era irresistibile nei primi anni '60 per il suo potere indiscriminato di:

  • saltare ovunque in qualsiasi blocco di codice
    • funzione non eseguita dall'inizio
    • loop non eseguiti dall'inizio
    • inizializzazione variabile saltata
  • saltare lontano da qualsiasi blocco di codice senza alcuna possibile pulizia.

Ciò non ha più nulla a che fare con l' gotoaffermazione dei linguaggi moderni, la cui esistenza è semplicemente dovuta al supporto della creazione di strutture di codice diverse da quelle fornite dal linguaggio.

In particolare, il primo punto principale sopra è più consentito e il secondo viene ripulito (se si gotoesce da un blocco, lo stack viene svolto correttamente e tutti i distruttori appropriati vengono chiamati)

Puoi fare riferimento a questa risposta per avere un'idea di come anche il codice che non utilizza goto possa essere illeggibile. Il problema non è quello di andare in se stesso, ma il suo cattivo utilizzo.

Posso scrivere un intero programma senza usare if, solo for. Certo, non sarà ben leggibile, un aspetto goffo e inutilmente complicato.

Ma il problema non lo è for. Sono io.

Cose come break, continue, throw, bool needed=true; while(needed) {...}, ecc stanno notando più di mascherarsi goto per fuggire lontano dalle scimitarre degli Zeloti Djikstrarian, che -50 anni dopo l'invenzione della moderna laguages- vogliono ancora i loro prigionieri. Hanno dimenticato di cosa stava parlando Djikstra, ricordano solo il titolo della sua nota (GOTO considerato dannoso, e non era nemmeno il suo titolo onw: è stato modificato dall'editore) e la colpa e la bash, bash e la colpa di ogni contratto che hanno quei 4 lettera posta in sequenza.

È il 2011: è tempo di capire che gotoè importante notare l' GOTOaffermazione di cui Djikstra è stata avvincente.


1
"Cose come rompere, continuare, lanciare, bool necessario = vero; mentre (necessario) {...}, ecc. Stanno notando qualcosa di più che andare in maschera" Non sono d'accordo. Preferirei "cose ​​come break etc sono limitate a goto", o qualcosa del genere. Il problema principale con goto è che di solito è troppo potente. Nella misura in cui l'attuale goto è meno potente di quello di Dijkstra, la tua risposta va bene con me.
Muhammad Alkarouri,

2
@MuhammadAlkarouri: sono totalmente d'accordo. Hai appena trovato una formulazione migliore per esprimere esattamente il mio concetto. Il mio punto è che a volte tali restrizioni non possono essere applicabili e il tipo di restrizione di cui hai bisogno non è nella lingua. Quindi una cosa più "potente", meno specializzata, è ciò che ti rende in grado di risolvere il problema. Ad esempio, Java break nnon è disponibile in C ++, quindi goto escape, anche se escapenon è necessario essere appena usciti dal ciclo n-ple.
Emilio Garavaglia,

4

Lo strano goto qui o là, purché sia ​​locale a una funzione, raramente danneggia significativamente la leggibilità. Ne beneficia spesso attirando l'attenzione sul fatto che in questo codice sta succedendo qualcosa di insolito che richiede l'uso di una struttura di controllo piuttosto rara.

Se i goto (locali) danneggiano significativamente la leggibilità, di solito è un segno che la funzione contenente il goto è diventata troppo complessa.

L'ultima goto che ho inserito in un pezzo di codice C è stata quella di costruire una coppia di anelli ad incastro. Non rientra nella normale definizione di utilizzo "accettabile", ma la funzione è risultata significativamente più piccola e più chiara di conseguenza. Per evitare il goto avrebbe richiesto una violazione particolarmente disordinata di DRY.


1
La risposta a questa domanda è stata espressa in modo molto conciso nel vecchio slogan "Lay's Potato Chips": "Scommetto che non puoi mangiarne solo uno". Nel tuo esempio, hai due loop di "interblocco" (qualunque cosa significhi, e sto scommettendo che non è carino), e il goto ti ha dato un risultato economico. E il ragazzo della manutenzione, che deve modificare quel codice dopo essere stato colpito da un autobus? Il goto renderà la sua vita più facile o più difficile? È necessario ripensare quei due loop?
John R. Strohm,

3

Penso che l'intero problema sia stato il caso di abbaiare l'albero sbagliato.

Il GOTO in quanto tale non mi sembra problematico, ma piuttosto spesso è un sintomo di un vero peccato: il codice spaghetti.

Se il GOTO causa l'attraversamento importante delle linee di controllo del flusso, allora non va bene, punto. Se non attraversa le linee di controllo del flusso è innocuo. Nella zona grigia in mezzo abbiamo cose come i salvataggi del ciclo, ci sono ancora alcune lingue che non hanno aggiunto costrutti che coprono tutti i casi grigi legittimi.

L'unico caso in cui mi sono ritrovato a usarlo effettivamente da molti anni è il caso del ciclo in cui il punto decisionale si trova nel mezzo del ciclo. Ti rimane un codice duplicato, una bandiera o un GOTO. Trovo la soluzione GOTO la migliore delle tre. Non vi è alcun incrocio di linee di controllo del flusso qui, è innocuo.


Il caso a cui alludi è a volte soprannominato "loop and half" o "N plus one half loop" ed è un famoso caso d'uso per gli gotos che saltano in un loop (se la lingua lo consente; dall'altro caso, saltare fuori dal giro nel mezzo, di solito è banale senza goto).
Konrad Rudolph,

(Ahimè, la maggior parte delle lingue proibisce di saltare in un loop.)
Konrad Rudolph

@KonradRudolph: se si implementa il "loop" con GOTO, non esiste un'altra struttura di loop in cui saltare.
Loren Pechtel,

1
Penso gotoche gridare IL FLUSSO DI CONTROLLO QUI NON SI ADATTA A MODELLI NORMALI DI PROGRAMMAZIONE STRUTTURATA . Poiché i flussi di controllo che si adattano a schemi di programmazione strutturati sono più facili da ragionare rispetto a quelli che non lo fanno, si dovrebbe tentare di usare tali schemi quando possibile; se il codice si adatta a tali modelli, non si dovrebbe gridare che non lo fa. D'altra parte, nei casi in cui il codice non si adatta davvero a tali schemi, urlare al riguardo potrebbe essere meglio che fingere che il codice si adatti quando non lo fa davvero.
supercat

1

Sì, il goto può essere utilizzato a beneficio dell'esperienza dello sviluppatore: http://adamjonrichardson.com/2012/02/06/long-live-the-goto-statement/

Tuttavia, proprio come con qualsiasi strumento potente (puntatori, ereditarietà multipla, ecc.), Bisogna essere disciplinati usandolo. L'esempio fornito nel collegamento utilizza PHP, che limita l'utilizzo del costrutto goto alla stessa funzione / metodo e disabilita la possibilità di saltare in un nuovo blocco di controllo (ad es. Loop, istruzione switch, ecc.)


1

Dipende dalla lingua. È ancora ampiamente usato nella programmazione Cobol, per esempio. Ho anche lavorato su un dispositivo Barionet 50, il cui linguaggio di programmazione del firmware è un dialetto BASIC iniziale che ovviamente richiede l'uso di Goto.


0

Direi di no. Se trovi la necessità di utilizzare GOTO, scommetto che è necessario riprogettare il codice.


3
Forse, ma non hai fornito alcun ragionamento
Casebash,

Dijkstra ha fornito il ragionamento, in dettaglio, decenni fa.
John R. Strohm,

2
Solo dogma. Nessuna risonanza. Nota: Djikstra NON È PIÙ UNA RESON VALIDA: si riferiva all'istruzione BASIC o FORTRAN GOTO dei primi anni '60. Non hanno nulla a che fare con i gots-s veramente anemici (rispetto al potere di quelli) di oggi.
Emilio Garavaglia,

0

gotopuò essere utile quando si esegue il porting del codice dell'assemblatore legacy su C. In primo luogo, una conversione istruzione per istruzione in C, che utilizza gotocome sostituto branchdell'istruzione dell'assemblatore , può consentire il porting molto rapido.


-1

Direi di no. Dovrebbero sempre essere sostituiti con uno strumento più specifico al problema che il goto viene utilizzato per risolvere (a meno che non sia disponibile nella tua lingua). Ad esempio, le dichiarazioni di interruzione e le eccezioni risolvono i problemi precedentemente risolti dall'escaping del loop e dalla gestione degli errori.


Direi che quando le lingue hanno entrambe queste caratteristiche, in gotogenere è assente da quelle lingue.
Chinmay Kanchi,

Ma è perché non ne hanno bisogno, o perché la gente pensa di non averne bisogno?
Michael K,

I tuoi punti di vista sono bigotti. Ci sono molti casi in cui gotoè la cosa più vicina alla semantica del dominio stesso problema, qualsiasi altra cosa sarebbe un trucco sporco.
SK-logic,

@ SK-logic: non credo che la parola "bigotto" significhi ciò che pensi significhi.
Keith Thompson,

1
@Keith, "intollerantemente devoto alle proprie opinioni e ai propri pregiudizi" - questo è ciò che osservo costantemente qui, con questo lotto di botte alla rovina. "Dovrebbero sempre essere sostituiti" sono le parole dei fanatici, almeno. Niente di simile a un'opinione corretta e corretta, ma solo un puro fanatismo. Questo "sempre" non lascia spazio ad alcuna opinione alternativa, vedi?
Logica SK
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.