Cosa c'è di così brutto con goto quando viene utilizzato per questi casi ovvi e pertinenti?


40

Ho sempre saputo che gotoè qualcosa di brutto, rinchiuso in un seminterrato da qualche parte per non essere mai visto per sempre, ma oggi ho incontrato un esempio di codice che ha perfettamente senso usare goto.

Ho un IP in cui devo verificare se si trova in un elenco di IP e quindi procedere con il codice, altrimenti genera un'eccezione.

<?php

$ip = '192.168.1.5';
$ips = [
    '192.168.1.3',
    '192.168.1.4',
    '192.168.1.5',
];

foreach ($ips as $i) {
    if ($ip === $i) {
        goto allowed;
    }
}

throw new Exception('Not allowed');

allowed:

...

Se non uso, gotoallora devo usare alcune variabili come

$allowed = false;

foreach ($ips as $i) {
    if ($ip === $i) {
        $allowed = true;
        break;
    }
}

if (!$allowed) {
    throw new Exception('Not allowed');
}

La mia domanda è cosa c'è di così brutto gotoquando viene utilizzato per casi così ovvi e imo rilevanti?


I commenti non sono per una discussione estesa; questa conversazione è stata spostata in chat .
maple_shaft

Risposte:


120

GOTO stesso non è un problema immediato, sono le macchine a stati impliciti che le persone tendono ad implementare con esso. Nel tuo caso, vuoi un codice che controlli se l'indirizzo IP è nell'elenco degli indirizzi consentiti, quindi

if (!contains($ips, $ip)) throw new Exception('Not allowed');

quindi il tuo codice vuole verificare una condizione. L'algoritmo per implementare questo controllo non dovrebbe preoccupare qui, nello spazio mentale del tuo programma principale il controllo è atomico. Ecco come dovrebbe essere.

Ma se inserisci il codice che esegue il controllo nel tuo programma principale, lo perdi. Si introduce lo stato mutabile, in modo esplicito:

$list_contains_ip = undef;        # STATE: we don't know yet

foreach ($ips as $i) {
  if ($ip === $i) {
      $list_contains_ip = true;   # STATE: positive
      break;
  }
                                  # STATE: we still don't know yet, huh?                                                          
                                  # Well, then...
  $list_contains_ip = false;      # STATE: negative
}

if (!$list_contains_ip) {
  throw new Exception('Not allowed');
}

dov'è la $list_contains_iptua unica variabile di stato, o implicitamente:

                             # STATE: unknown
foreach ($ips as $i) {       # What are we checking here anyway?
  if ($ip === $i) {
    goto allowed;            # STATE: positive
  }
                             # STATE: unknown
}
                             # guess this means STATE: negative
throw new Exception('Not allowed');

allowed:                     # Guess we jumped over the trap door

Come vedi, c'è una variabile di stato non dichiarata nel costrutto GOTO. Questo non è un problema in sé, ma queste variabili di stato sono come dei ciottoli: portarne uno non è difficile, portare una borsa piena di loro ti farà sudare. Il tuo codice non rimarrà lo stesso: il mese prossimo ti verrà chiesto di distinguere tra indirizzi privati ​​e pubblici. Il mese successivo, il codice dovrà supportare intervalli IP. L'anno prossimo qualcuno ti chiederà di supportare gli indirizzi IPv6. In pochissimo tempo, il tuo codice sarà simile al seguente:

if ($ip =~ /:/) goto IP_V6;
if ($ip =~ /\//) goto IP_RANGE;
if ($ip =~ /^10\./) goto IP_IS_PRIVATE;

foreach ($ips as $i) { ... }

IP_IS_PRIVATE:
   foreach ($ip_priv as $i) { ... }

IP_V6:
   foreach ($ipv6 as $i) { ... }

IP_RANGE:
   # i don't even want to know how you'd implement that

ALLOWED:
   # Wait, is this code even correct?
   # There seems to be a bug in here.

E chiunque debba eseguire il debug di quel codice maledirà te e i tuoi figli.

Dijkstra dice così:

L'uso sfrenato dell'istruzione go to ha come conseguenza immediata che diventa terribilmente difficile trovare un insieme significativo di coordinate in cui descrivere l'avanzamento del processo.

Ed è per questo che GOTO è considerato dannoso.


22
Questa $list_contains_ip = false;affermazione sembra fuori luogo
Bergi,

1
Una borsa piena di ciottoli è facile, ho una borsa comoda per tenerli. : p
whatsisname

13
@Bergi: hai ragione. Ciò dimostra quanto sia facile rovinare queste cose.
wallenborn,

6
@whatsisname Quella borsa è chiamata funzione / classe / pacchetto per tenerli. usare le goto è come destreggiarle e buttare via la borsa.
Falco,

5
Adoro la citazione di Dijkstra, non l'avevo mai sentita dire così, ma è davvero un buon punto. Invece di avere un flusso praticamente dall'alto verso il basso, improvvisamente hai qualcosa che può saltare in tutta la pagina in modo casuale. Metterlo in modo succinto è piuttosto impressionante.
Bill K,

41

Ci sono alcuni casi d'uso legittimi per GOTO. Ad esempio per la gestione degli errori e la pulizia in C o per l'implementazione di alcune forme di macchine a stati. Ma questo non è uno di questi casi. Il secondo esempio è IMHO più leggibile, ma ancora più leggibile sarebbe quello di estrarre il ciclo in una funzione separata e poi tornare quando trovi una corrispondenza. Ancora meglio sarebbe (in pseudocodice, non conosco la sintassi esatta):

if (!in_array($ip, $ips)) throw new Exception('Not allowed');

Quindi cosa c'è di così brutto GOTO? La programmazione strutturata utilizza funzioni e strutture di controllo per organizzare il codice in modo che la struttura sintattica rifletta la struttura logica. Se qualcosa viene eseguito solo in modo condizionale, verrà visualizzato in un blocco di istruzioni condizionali. Se qualcosa viene eseguito in un loop, apparirà in un blocco loop. GOTOti consente di aggirare la struttura sintattica saltando arbitrariamente, rendendo così il codice molto più difficile da seguire.

Naturalmente se non hai altra scelta che usi GOTO, ma se lo stesso effetto può essere ottenuto con funzioni e strutture di controllo, è preferibile.


5
@jameslarge Ti interessa it's easier to write good optimizing compilers for "structured" languages.elaborare? Come contro esempio, la gente di Rust ha introdotto MIR , una rappresentazione intermedia nel compilatore che sostituisce specificamente i loop, continua, si interrompe e simili con goto, perché è più semplice da controllare e ottimizzare.
settembre

6
"se non hai altra scelta usi GOTO" Questo sarebbe estremamente raro. Onestamente non riesco a pensare a un caso in cui non hai altra scelta, al di fuori di restrizioni completamente ridicole che impediscono il refactoring del codice esistente o simili assurdità.
jpmc26,

8
Inoltre, IMO, emulando a gotocon un nido di ratti di bandiere e condizionali (come nel secondo esempio dell'OP) è molto meno leggibile rispetto alle juts usando a goto.

3
@ 8bittree: la programmazione strutturata è intesa per la lingua di origine, non per la lingua di destinazione del compilatore. La lingua di destinazione per i compilatori nativi è istruzioni della CPU, che è decisamente non "strutturato", nel senso linguaggio di programmazione.
Robert Harvey,

4
@ 8bittree La risposta alla tua domanda sul MIR è nel link che hai fornito: "Ma va bene avere un tale costrutto nel MIR, perché sappiamo che verrà usato solo in modi particolari, come per esprimere un ciclo o una pausa ". In altre parole, poiché goto non è consentito in Rust, sanno che è strutturato e anche la versione intermedia con goto. Alla fine, il codice deve scendere per andare come i comandi della macchina. Quello che ottengo dal tuo link è che stanno semplicemente aggiungendo un passaggio incrementale nella conversione da un linguaggio di alto livello a un livello di basso livello.
JimmyJames,

17

Come altri hanno già detto, il problema non è con gotose stesso; il problema è con come le persone usano gotoe come può rendere il codice più difficile da capire e mantenere.

Supponi il seguente frammento di codice:

       i = 4;
label: printf( "%d\n", i );

Per quale valore viene stampato i? Quando viene stampato? Fino a quando non si tiene conto di ogni istanza di goto labelnella propria funzione, non si può sapere. La semplice presenza di quell'etichetta distrugge la tua capacità di eseguire il debug del codice con una semplice ispezione. Per piccole funzioni con uno o due rami, non c'è molto problema. Per funzioni non piccole ...

All'inizio degli anni '90 ci è stato dato un mucchio di codice C che guidava un display grafico 3d e ci diceva di farlo correre più veloce. E 'stato solo circa 5000 righe di codice, ma tutto di esso era in main, e l'autore ha usato circa 15 o giù di lì gotos ramificazione in entrambe le direzioni. Inizialmente, questo era un codice errato, ma la presenza di quelli gotopeggiorava le cose. Il mio collega ha impiegato circa 2 settimane per risolvere il flusso di controllo. Ancora meglio, questi gotorisultati hanno prodotto un codice così strettamente accoppiato con se stesso che non abbiamo potuto apportare modifiche senza rompere qualcosa.

Abbiamo provato a compilare con l'ottimizzazione di livello 1, e il compilatore ha mangiato tutta la RAM disponibile, quindi tutti gli swap disponibili, e poi ha preso il panico nel sistema (che probabilmente non aveva nulla a che fare con gli gotostessi, ma mi piace buttare lì quell'aneddoto).

Alla fine, abbiamo offerto al cliente due opzioni: riscriviamo tutto da zero o acquistiamo hardware più veloce.

Hanno comprato hardware più veloce.

Regole di Bode per l'utilizzo di goto:

  1. Diramazione solo in avanti;
  2. Non bypassare le strutture di controllo (cioè non ramificarsi nel corpo di un'istruzione ifo foro while);
  3. Non utilizzare gotoal posto di una struttura di controllo

Ci sono casi in cui a goto è la risposta giusta, ma sono rari (uscire da un ciclo profondamente annidato riguarda l'unico posto in cui lo userei).

MODIFICARE

Espandendo su quest'ultima affermazione, ecco uno dei pochi casi d'uso validi per goto. Supponiamo di avere la seguente funzione:

T ***myalloc( size_t N, size_t M, size_t P )
{
  size_t i, j, k;

  T ***arr = malloc( sizeof *arr * N );
  for ( i = 0; i < N; i ++ )
  {
    arr[i] = malloc( sizeof *arr[i] * M );
    for ( j = 0; j < M; j++ )
    {
      arr[i][j] = malloc( sizeof *arr[i][j] * P );
      for ( k = 0; k < P; k++ )
        arr[i][j][k] = initial_value();
    }
  }
  return arr;
}

Ora, abbiamo un problema: cosa succede se una delle mallocchiamate non riesce a metà? È improbabile che un evento del genere, non vogliamo restituire un array parzialmente allocato, né vogliamo semplicemente uscire dalla funzione con un errore; vogliamo ripulire dopo noi stessi e deallocare qualsiasi memoria parzialmente allocata. In una lingua che genera un'eccezione su un'allocazione errata, è abbastanza semplice: basta scrivere un gestore di eccezioni per liberare ciò che è già stato allocato.

In C, non hai una gestione strutturata delle eccezioni; devi controllare il valore di ritorno di ogni mallocchiamata e intraprendere le azioni appropriate.

T ***myalloc( size_t N, size_t M, size_t P )
{
  size_t i, j, k;

  T ***arr = malloc( sizeof *arr * N );
  if ( arr )
  {
    for ( i = 0; i < N; i ++ )
    {
      if ( !(arr[i] = malloc( sizeof *arr[i] * M )) )
        goto cleanup_1;

      for ( j = 0; j < M; j++ )
      {
        if ( !(arr[i][j] = malloc( sizeof *arr[i][j] * P )) )
          goto cleanup_2;

        for ( k = 0; k < P; k++ )
          arr[i][j][k] = initial_value();
      }
    }
  }
  goto done;

  cleanup_2:
    // We failed while allocating arr[i][j]; clean up the previously allocated arr[i][j]
    while ( j-- )
      free( arr[i][j] );
    free( arr[i] );
    // fall through

  cleanup_1:
    // We failed while allocating arr[i]; free up all previously allocated arr[i][j]
    while ( i-- )
    {
      for ( j = 0; j < M; j++ )
        free( arr[i][j] );
      free( arr[i] );
    }

    free( arr );
    arr = NULL;

  done:
    return arr;
}

Possiamo farlo senza usare goto? Certo che possiamo - richiede solo un po 'di contabilità extra (e, in pratica, questo è il percorso che prenderei). Ma se stai cercando luoghi in cui l'utilizzo di a gotonon è immediatamente un segno di cattiva pratica o design, questo è uno dei pochi.


Mi piacciono queste regole. Non userei ancora gotonemmeno in conformità con queste regole — non lo userei affatto a meno che non sia l'unica forma di ramificazione disponibile nella lingua — ma posso vedere che non sarebbe un incubo troppo debug gotoè se fossero stati scritti secondo queste regole.
Wildcard il

se hai bisogno di uscire da un ciclo profondamente annidato, devi semplicemente rendere quel ciclo un'altra funzione e tornare
bunyaCloven

6
Come qualcuno una volta lo ha riassunto per me: "Non è un problema irrinunciabile, è un problema di origine". Come specialista dell'ottimizzazione del software concordo sul fatto che il flusso di controllo non strutturato può generare compilatori (e umani!) Per un ciclo. Una volta ho trascorso ore a decifrare una semplice macchina a stati (quattro su cinque stati) in una funzione BLAS, che utilizzava varie forme di GOTO, tra cui le famose goto assegnate e aritmetiche di Fortran, se ricordo bene.
njuffa,

@njuffa "In qualità di specialista di ottimizzazione del software, sono d'accordo sul fatto che il flusso di controllo non strutturato può lanciare compilatori (e umani!) per un ciclo." - ulteriori dettagli? Ho pensato che praticamente ogni compilatore di ottimizzazione al giorno d'oggi utilizza SSA come IR, il che significa che utilizza un codice simile a goto sotto.
Maciej Piechotka,

@MaciejPiechotka Non sono un ingegnere di compilazione. Sì, tutti i compilatori moderni sembrano usare SSA nella loro rappresentazione intermedia. I compilatori preferiscono costrutti di controllo single-entry-single-exit, non sei sicuro di come giochi SSA in questo? Osservando il codice macchina generato e l'utilizzo delle risorse del compilatore e le discussioni con gli ingegneri del compilatore il flusso di controllo non strutturato interferisce con l'ottimizzazione delle trasformazioni del codice, presumibilmente a causa del problema "come-from". L'utilizzo delle risorse (tempo, memoria) può avere un impatto negativo sulle prestazioni del codice se il compilatore decide di saltare un'ottimizzazione in quanto troppo costosa.
njuffa,

12

return, break, continueE throw/ catchsono tutti essenzialmente goto - tutti controllo di trasferimento ad un altro pezzo di codice e potrebbero tutti essere attuate con goto - in realtà l'ho fatto una volta in un progetto scolastico, un istruttore di PASCAL stava dicendo quanto meglio Pascal era di semplice a causa della struttura ... quindi dovevo essere contrario ...

La cosa più importante dell'ingegneria del software (userò questo termine per la codifica per fare riferimento a una situazione in cui sei pagato da qualcuno per creare una base di codice insieme ad altri ingegneri che richiedono miglioramenti e manutenzione costanti) è rendere il codice leggibile- -prenderlo per fare qualcosa è quasi secondario. Il tuo codice verrà scritto una sola volta ma, nella maggior parte dei casi, le persone trascorreranno giorni e settimane a rivisitare / riapprendimento, migliorandolo e risolvendolo - e ogni volta che (o tu) dovranno ricominciare da zero e provare a ricordare / capire il tuo codice.

La maggior parte delle funzionalità che sono state aggiunte alle lingue nel corso degli anni è di rendere il software più gestibile, non più facile da scrivere (anche se alcune lingue vanno in quella direzione - spesso causano problemi a lungo termine ...).

Rispetto a dichiarazioni di controllo del flusso simili, i GOTO possono essere quasi altrettanto facili da seguire al meglio (un singolo goto usato in un caso come suggerisci) e un incubo se abusati - e sono molto facilmente abusati ...

Quindi, dopo aver affrontato gli incubi degli spaghetti per alcuni anni, abbiamo appena detto "No", come comunità non accetteremo questo - troppe persone lo incasinano se gli viene dato un piccolo margine di manovra - questo è davvero l'unico problema. Potresti usarli ... ma anche se è il caso perfetto, il prossimo ragazzo supporrà che sei un programmatore terribile perché non capisci la storia della comunità.

Molte altre strutture sono state sviluppate solo per rendere il tuo codice più comprensibile: funzioni, oggetti, scoping, incapsulamento, commenti (!) ... così come i modelli / processi più importanti come "DRY" (che impedisce la duplicazione) e "YAGNI" (Ridurre l'eccessiva generalizzazione / complicazione del codice) - tutto ciò che importa davvero solo per il ragazzo SUCCESSIVO a leggere il tuo codice (chi probabilmente sarai tu - dopo aver dimenticato la maggior parte di ciò che hai fatto in primo luogo!)


3
Sto votando questo solo per la frase, "La cosa più importante ... è rendere il codice leggibile; farlo fare qualcosa è quasi secondario." Direi in modo leggermente diverso; non è tanto rendere il codice leggibile quanto renderlo semplice . Ma in entrambi i casi, è oh-così-vero che lo rende fare qualcosa è secondario.
Wildcard il

" , E / sono tutti essenzialmente goto" Non sono d'accordo, l'ex rispetto i principi della programmazione strutturata (anche se si potrebbe discutere di eccezioni), go-to sicuramente non lo fa. Alla luce di questa domanda, non si ottiene molto da questa osservazione. returnbreakcontinuethrowcatch
Eric,

@eric È possibile modellare qualsiasi affermazione con GOTO. Potresti assicurarti che i GOTO siano tutti d'accordo con i principi di programmazione strutturata: hanno semplicemente una diversa sintassi e devono, per duplicare esattamente la funzionalità, includere altre operazioni come "if". Il vantaggio delle versioni strutturate è che sono limitate a supportare solo determinati target: lo sai quando vedi un punto approssimativo dove si troverà il target. Il motivo per cui ho citato è stato quello di sottolineare che è l'abusabilità il problema, non che non possa essere usato correttamente.
Bill K,

7

GOTOè uno strumento. Può essere usato per il bene o per il male.

Ai vecchi tempi, con FORTRAN e BASIC, era quasi l' unico strumento.

Quando guardi il codice di quei giorni, quando vedi un GOTOdevi capire perché è lì. Può far parte di un linguaggio standard che puoi capire rapidamente ... o può far parte di una struttura di controllo da incubo che non avrebbe mai dovuto essere. Non lo sai fino a quando non hai guardato, ed è facile sbagliarsi.

Le persone volevano qualcosa di meglio e furono inventate strutture di controllo più avanzate. Questi riguardavano la maggior parte dei casi d'uso e le persone che erano state bruciate dai cattivi GOTOvolevano bandirli completamente.

Ironia della sorte, GOTOnon è poi così male quando è raro. Quando ne vedi uno, sai che sta succedendo qualcosa di speciale, ed è facile trovare l'etichetta corrispondente poiché è l'unica etichetta nelle vicinanze.

Avanti veloce ad oggi. Sei un docente che insegna programmazione. Si potrebbe dire "Nella maggior parte dei casi è necessario utilizzare le nuove avanzate costrutti, ma in alcuni casi un semplice GOTOpuò essere più leggibile." Gli studenti non lo capiranno. Abuseranno GOTOper rendere il codice illeggibile.

Invece dici " GOTOcattivo. GOTOMale. GOTOFallisci esame". Gli studenti capiranno che !


4
Questo è vero per tutte le cose deprecate. Globali. Nomi variabili di una lettera. Codice automodificante. Eval. Persino, sospetto, input dell'utente non filtrato. Una volta che ci siamo condizionati per evitare cose come la peste, allora siamo nella posizione adatta per valutare se un loro uso, ben segnalato, potrebbe essere la soluzione corretta a un problema specifico. Prima di allora, è meglio trattarli come semplicemente vietati.
Dewi Morgan,

4

Con l'eccezione di goto, tutti i costrutti di flusso in PHP (e nella maggior parte dei linguaggi) hanno un ambito gerarchico.

Immagina un po 'di codice esaminato attraverso gli occhi socchiusi:

a;
foo {
    b;
}
c;

Indipendentemente da quale controllo costrutto fooè ( if, whileecc), ci sono solo alcuni ordini consentiti per a, be c.

Potresti avere a- b- c, o a- c, o addirittura a- b- b- b- c. Ma non potresti mai avere b- co a- b- a- c.

... a meno che tu non abbia goto.

$a = 1;
first:
echo 'a';
if ($a === 1) {
    echo 'b';
    $a = 2;
    goto first;
}
echo 'c'; 

goto(in particolare all'indietro goto) può essere abbastanza fastidioso che è meglio lasciarlo solo e usare costrutti di flusso gerarchici e bloccati.

gotos hanno un posto, ma soprattutto come micro-ottimizzazioni in lingue di basso livello. IMO, non c'è un buon posto in PHP.


Cordiali saluti, il codice di esempio può essere scritto anche meglio di uno dei tuoi suggerimenti.

if(!in_array($ip, $ips, true)) {
    throw new Exception('Not allowed');
}

2

Nelle lingue di basso livello GOTO è inevitabile. Ma ad alto livello dovrebbe essere evitato (nel caso in cui la lingua lo supporti) perché rende i programmi più difficili da leggere .

Tutto si riduce a rendere il codice più difficile da leggere. Sono supportate le lingue di alto livello per facilitare la lettura del codice rispetto alle lingue di basso livello come, ad esempio, assemblatore o C.

GOTO non provoca il riscaldamento globale né provoca povertà nel terzo mondo. Rende solo il codice più difficile da leggere.

La maggior parte dei linguaggi moderni ha strutture di controllo che rendono GOTO superfluo. Alcuni come Java non ce l'hanno nemmeno.

In effetti, il termine codice spaguetti deriva da cause di codice contorte, difficili da seguire da strutture di ramificazione non strutturate.


6
gotopuò rendere il lettore di codice a leggere. Quando hai creato variabili extra e rami nidificati rispetto a un singolo salto, il codice è molto più difficile da leggere e comprendere.
Matthew Whited,

@MatthewWhited Forse se hai un singolo goto in un metodo a dieci righe. Ma la maggior parte dei metodi non sono lunghi dieci righe e la maggior parte delle volte quando le persone usano GOTO non lo usano "solo una volta".
Tulains Córdova,

2
@MatthewWhited In effetti, il termine codice spaguetti deriva da un codice contorto, difficile da seguire, causato da strutture di ramificazione non strutturate. O il codice spaguetti è più facile da rosso adesso? Può essere. Il vinile sta tornando, perché non sarebbe GOTO.
Tulains Córdova,

3
@Tulain Devo credere che esistano cose così brutte perché le persone continuano a lamentarsi allora, ma la maggior parte delle volte che vedo gotoemergere non sono esempi di codice spaghetti contorto, ma piuttosto cose piuttosto semplici, che spesso emulano schemi di flusso di controllo in lingue che don non hanno la sintassi per questo: i due esempi più comuni sono la rottura di più loop e l'esempio nell'OP dove gotoviene utilizzato per implementare for- else.

1
Il codice spaghetti non strutturato proviene da lingue in cui non esistono funzioni / metodi. gotoè ottimo anche per cose come tentativi di eccezione, rollback, macchine a stati.
Matthew Whited,

1

Niente di sbagliato nelle gotodichiarazioni stesse. I torti sono con alcune persone che usano in modo inappropriato l'affermazione.

Oltre a quanto detto da JacquesB (gestione degli errori in C), stai usando gotoper uscire da un ciclo non nidificato, cosa che puoi fare usando break. In questo caso è meglio usarlo break.

Ma se avessi uno scenario di loop nidificato, l'utilizzo gotosarebbe più elegante / più semplice. Per esempio:

$allowed = false;

foreach ($ips as $i) {
    foreach ($ips2 as $i2) {
        if ($ip === $i && $ip === $i2) {
            $allowed = true;
            goto out;
        }
    }
}

out:

if (!$allowed) {
    throw new Exception('Not allowed');
}

L'esempio sopra non ha senso nel tuo problema specifico, perché non hai bisogno di un ciclo nidificato. Ma spero che tu veda solo la parte del ciclo nidificato.

Punto bonous: se la tua lista di IP è piccola, il tuo metodo va bene. Ma se l'elenco cresce, sappi che il tuo approccio presenta una peggiore complessità runtime asintotica di O (n) . Man mano che la tua lista cresce, potresti voler usare un metodo diverso che raggiunge O (log n) (come una struttura ad albero) o O (1) (una tabella hash senza collisioni).


6
Non sono sicuro di PHP, ma molte lingue supporteranno l'utilizzo breake continuecon un numero (per interrompere / continuare quel numero di strati di loop) o un'etichetta (per interrompere / continuare il ciclo con quell'etichetta), una delle quali dovrebbe essere generalmente preferibile utilizzare gotoper loop nidificati.
settembre

@ 8bittree buon punto. Non sapevo che l' interruzione di PHP fosse quella fantasia. So solo che l' interruzione di C non è così elaborata. Anche l' interruzione di Perl (la chiamano per ultima ) era simile a quella di C (cioè non fantasiosa come quella di PHP) ma dopo un certo periodo è diventata fantasiosa. Immagino che la mia risposta stia diventando sempre meno utile poiché più lingue stanno introducendo versioni fantasiose di break :)
caveman

3
un'interruzione con un valore di profondità non risolverebbe il problema senza la variabile aggiuntiva. Una variabile a sé stante che crea un ulteriore livello di complessità.
Matthew Whited,

Ovviamente, solo perché hai dei loop nidificati non significa che hai bisogno di un goto, ci sono altri modi per gestire elegantemente quella situazione.
NPSF3000,

@ NPSF3000 Potresti per favore offrire un esempio di un ciclo nidificato che puoi uscire senza usare un'istruzione goto , mentre usi anche un linguaggio che non ha una versione stravagante di break che specifica la profondità?
cavernicolo

0

Con goto posso scrivere codice più veloce!

Vero. Non importa.

Goto esiste in assemblea! Lo chiamano semplicemente jmp.

Vero. Non importa.

Goto risolve i problemi più semplicemente.

Vero. Non importa.

Nelle mani di un codice sviluppatore disciplinato che utilizza goto può essere più facile da leggere.

Vero. Tuttavia, sono stato quel programmatore disciplinato. Ho visto cosa succede al codice nel tempo. Gotoinizia bene. Quindi inizia la tentazione di riutilizzare il codice. Abbastanza presto mi ritrovo in un punto di interruzione che non ha la minima idea di cosa stia succedendo anche dopo aver visto lo stato del programma. Gotorende difficile ragionare sul codice. Abbiamo lavorato molto duramente creazione while, do while, for, for each switch, subroutines, functions, e altro ancora tutto perché a fare questa roba con ifed gotoè difficile sul cervello.

Quindi no. Non vogliamo guardare goto. Certo è vivo e vegeto nel binario ma non abbiamo bisogno di vederlo alla fonte. In effetti, ifsta iniziando a sembrare un po 'traballante.


1
"Goto risolve i problemi più semplicemente." / "Vero. Non importa." - Odio vedere il tuo codice in cui eviti deliberatamente soluzioni semplici a favore di ciò che è più estensibile. (Sentito parlare di YAGNI?)
user253751

1
@Wildcard if e goto sono modi molto semplici per risolvere i problemi meglio risolti in altri modi. Sono frutti bassi appesi. Non si tratta della lingua particolare. Gototi consente di astrarre un problema rendendolo un altro problema con un po 'di codici. Ifti permette di scegliere quell'astrazione. Ci sono modi migliori per astrarre e modi migliori per scegliere l'astrazione. Polimorfismo per uno.
candied_orange,

2
Anche se sono d'accordo con te, questa è una risposta scarsamente formulata. "Vero, non importa" non è un argomento convincente per nulla. Penso che il tuo messaggio principale sia questo: sebbene goto sia allettante per le persone che preferiscono utilizzare strumenti efficienti e potenti, porta in seguito a una quantità sorprendentemente enorme di tempo sprecato, anche quando si presta attenzione.
Artelius,

1
@artelius Il tema "Vero, non importa" non vuole essere convincente. Ha lo scopo di illustrare i molti punti che concederò per andare a goto e ancora scenderci contro. Pertanto, argomentare questi punti con me non ha senso. Dal momento che non è questo il mio punto. Capisci il punto?
candied_orange,

1
Credo solo che una buona risposta non solo dichiari la tua opinione, ma la spieghi e consenta al lettore di esprimere il proprio giudizio. Allo stato attuale, non è chiaro perché i punti positivi di Goto non siano sufficienti per superare i suoi problemi. La maggior parte di noi ha passato ore a strapparsi i capelli cercando di trovare bug che non comportano goto. Non è l'unico costrutto su cui è difficile ragionare. Qualcuno che pone questa domanda è probabilmente molto meno esperto e ha bisogno di cose messe in prospettiva.
Artelius,

-1

I linguaggi di assemblaggio in genere hanno solo salti condizionati / incondizionati (l'equivalente di GOTO. Le implementazioni precedenti di FORTRAN e BASIC non avevano istruzioni di blocco di controllo oltre un'iterazione contata (il ciclo DO), lasciando tutti gli altri flussi di controllo verso IF e GOTO. Il ciclo DO in queste lingue erano terminate da un'istruzione numericamente etichettata: di conseguenza, il codice scritto per queste lingue poteva essere, e spesso era, difficile da seguire e soggetto a errori.

Per sottolineare il punto, c'è l' affermazione " VIENI " inventata in modo faceto .

Non è praticamente necessario utilizzare GOTO in linguaggi come C, C ++, C #, PASCAL, Java, ecc .; si possono usare costruzioni alternative che saranno quasi sicuramente altrettanto efficienti e molto più mantenibili. È vero che un GOTO in un file sorgente non sarà un problema. Il problema è che non ci vogliono molti per rendere un'unità di codice difficile da seguire e soggetta a errori da mantenere. Ecco perché la saggezza accettata è quella di evitare GOTO quando possibile.

Questo articolo di Wikipedia sull'istruzione goto potrebbe essere utile

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.