Perché Go ha una dichiarazione "goto"


110

Sono stato sorpreso di scoprire che Go ha una dichiarazione "goto" . Mi è sempre stato insegnato che le affermazioni "goto" sono una cosa del passato e malvagie perché occludono il flusso effettivo di un programma e che le funzioni oi metodi sono sempre un modo migliore per controllare il flusso.

Devo essermi perso qualcosa. Perché Google l'ha incluso?


5
Ci sono momenti in cui hai davvero bisogno di un'istruzione goto. I Goto sono malvagi solo se usati indiscriminatamente. Ad esempio, se è molto difficile, se non impossibile, scrivere un parser di macchina a stati finiti senza istruzioni goto.
xbonez

5
Non è specifico per Go, ma per una buona discussione sul motivo per cui le lingue conservano l'affermazione e per vedere argomenti contro il suo utilizzo, controlla questo post . Ci sono alcuni buoni riferimenti collegati alla domanda. Modifica: eccone un altro .
Cᴏʀʏ

3
Per salvare l'OP dal grepping attraverso le discussioni SO fornite, ecco la discussione su LKML che riassume praticamente perché gotoè utile in alcuni casi. Leggi dopo aver studiato la risposta di @ Kissaki.
kostix


È utile implementare un modello di continuazione, in cui si salva dallo stack e si torna al punto in cui si trovava quando si desidera riprendere.
Justin Dennahower

Risposte:


78

Quando controlliamo effettivamente il codice sorgente della libreria standard Go, possiamo vedere dove le gotos sono effettivamente ben applicate.

Ad esempio, nel math/gamma.gofile, viene utilizzata l' gotoistruzione :

  for x < 0 {
    if x > -1e-09 {
      goto small
    }
    z = z / x
    x = x + 1
  }
  for x < 2 {
    if x < 1e-09 {
      goto small
    }
    z = z / x
    x = x + 1
  }

  if x == 2 {
    return z
  }

  x = x - 2
  p = (((((x*_gamP[0]+_gamP[1])*x+_gamP[2])*x+_gamP[3])*x+_gamP[4])*x+_gamP[5])*x + _gamP[6]
  q = ((((((x*_gamQ[0]+_gamQ[1])*x+_gamQ[2])*x+_gamQ[3])*x+_gamQ[4])*x+_gamQ[5])*x+_gamQ[6])*x + _gamQ[7]
  return z * p / q

small:
  if x == 0 {
    return Inf(1)
  }
  return z / ((1 + Euler*x) * x)
}

In gotoquesto caso, ci evita di introdurre un'altra variabile (booleana) usata solo per il flusso di controllo, controllata alla fine. In questo caso , l' gotoistruzione rende il codice effettivamente migliore da leggere e da seguire più facilmente (al contrario dell'argomento contro gotodi cui hai parlato).

Si noti inoltre che la gotodichiarazione ha un caso d'uso molto specifico. La specifica del linguaggio su goto afferma che non può saltare le variabili che entrano nello scope (che vengono dichiarate) e non può saltare in altri blocchi (di codice).


69
Nel tuo esempio, perché non introdurre semplicemente una funzione small(x,z)da chiamare? In questo modo non dobbiamo pensare a quali variabili sono accessibili small:nell'etichetta. Sospetto che il motivo sia che go manca ancora di alcuni tipi di supporto inlining nel compilatore.
Thomas Ahle

5
@ Jessta: Questo è ciò per cui abbiamo visibilità e possibilità, giusto?
Thomas Ahle,

6
@ThomasAhle Go non consente gotodi puntare a un'etichetta dopo che sono state introdotte nuove variabili. L'esecuzione dell'istruzione "goto" non deve far entrare nello scope alcuna variabile che non fosse già nello scope al punto goto.
km6zla

4
@ ogc-nick Spiacente, non sono stato chiaro, volevo dire che le funzioni possono essere dichiarate nell'ambito in cui sono necessarie, quindi non sono visibili al codice che non ne ha bisogno. Non stavo parlando di goto e scope.
Thomas Ahle,

4
@MosheRevah Il codice di riferimento non è ottimizzato per la leggibilità. È ottimizzato per prestazioni grezze, utilizzando un goto che si estende su 22 linee all'interno di una singola funzione. (E la proposta di Thomas Ahle è ancora più leggibile ai miei occhi.)
joel.neely

30

Goto è una buona idea quando nessuna delle funzionalità di controllo integrate fa esattamente quello che vuoi e quando puoi esprimere ciò che vuoi con un goto. (È un peccato in questi casi in alcune lingue quando non si dispone di goto. Si finisce per abusare di alcune funzionalità di controllo, utilizzare flag booleani o utilizzare altre soluzioni peggiori di goto.)

Se qualche altra funzione di controllo (usata in modo ragionevolmente ovvio) può fare quello che vuoi, dovresti usarla preferibilmente a goto. In caso contrario, sii audace e usa goto!

Infine vale la pena notare che il goto di Go ha alcune restrizioni progettate per evitare alcuni bug oscuri. Vedere queste limitazioni nelle specifiche.


7

Le dichiarazioni di Goto hanno ricevuto molto discredito dall'era del codice Spaghetti negli anni '60 e '70. Allora non c'era una metodologia di sviluppo software molto scarsa o nulla. Tuttavia i Goto non sono nativamente malvagi ma possono ovviamente essere usati in modo improprio e abusati da programmatori pigri o inesperti. Molti problemi con Gotos abusati possono essere risolti con processi di sviluppo come le revisioni del codice del team.

gotosono salti nello stesso modo tecnico di continue, breakereturn . Si potrebbe sostenere che queste sono affermazioni sono malvagie allo stesso modo, ma non lo sono.

Il motivo per cui il team Go ha incluso Gotos è probabilmente dovuto al fatto che si tratta di una primitiva comune di controllo del flusso. Inoltre, si spera, hanno concluso che lo scopo di Go esclude il fatto di non poter abusare di un linguaggio sicuro per gli idioti.


continue, breakE returnsono molto diversi in una chiave particolare: specificano solo "lasciano l'ambito di inclusione". Non solo incoraggiano, ma richiedono esplicitamente che lo sviluppatore consideri la struttura del proprio codice e faccia affidamento su primitive di programmazione strutturate (per cicli, funzioni e istruzioni switch). L'unico e unico vantaggio delle gotoistruzioni è che consentono di scrivere assembly in un HLL quando l'ottimizzatore del compilatore non è all'altezza del compito, ma questo ha un costo di leggibilità e manutenibilità.
Parthian Shot

Il motivo per cui questo è così sorprendente trovare in Go è che, in ogni altro caso in cui gli sviluppatori golang avevano una scelta tra paradigma di programmazione strutturata e "controllo di flusso eccezionale" / manipolazioni puntatore all'istruzione piace setjmp, longjmp, goto, e try / except / finallyhanno scelto di sbagliare sul lato di cautela. goto, fwict, è l'unica acquiescenza al flusso di controllo pre- "programmazione strutturata".
Parthian Shot
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.