2009-06-21 6 views
19

Sto risolvendo il problema 9 su Project Euler. Nella mia soluzione uso una dichiarazione "goto" per uscire da due cicli. Il problema è il seguente:sta usando goto un modo legittimo per uscire da due cicli?

Una tripletta di Pitagora è un insieme di tre numeri naturali, abc, per cui,

a^2 + b^2 = c^2

Ad esempio, 3^2 + 4^2 = 9 + 16 = 25 = 52.

Esiste esattamente una tripletta di Pitagora per cui a + b + c = 1000. Trova il prodotto abc.

La mia soluzione è in C++:

int a,b,c; 
const int sum = 1000; 
int result = -1; 
for (a = 1; a<sum; a++){ 
    for (b = 1; b < sum; b++){ 
      c = sum-a-b; 
      if (a*a+b*b == c*c){ 
       result = a*b*c; 
       goto found; 
      } 
    } 
} 
found: 
std::cout << "a:" << a << std::endl; 
std::cout << "b:" << b << std::endl; 
std::cout << "c:" << c << std::endl; 
std::cout <<"Result:" << result << std::endl; 

Dal dichiarazioni "Vai a" non sono molto popolari tra i programmatori C++, vorrei sapere, se questo potrebbe essere considerato un uso ragionevole di "goto" . O se c'è una soluzione migliore per il problema che non ha bisogno di "goto". Con ciò non intendo una soluzione che eviti solo "goto", ma che eviti "goto" in un modo che migliori l'algoritmo.

+13

mi piacciono quelle schede 8-space che stai usando .... molto sexy. –

+0

grazie, mi piacciono anche loro :-) – Lucas

+10

Con una scheda di 16 spazi nel mezzo ... –

risposta

45

return è un "strutturato" goto che molti programmatori trovano più accettabile! Quindi:

static int findit(int sum, int* pa, int* pb, int* pc) 
{ 
    for (int a = 1; a<sum; a++) { 
     for (int b = 1; b < sum; b++) { 
      int c = sum-a-b; 
      if (a*a+b*b == c*c) { 
       *pa = a; *pb = b; *pc = c; 
       return a*b*c; 
     } 
    } 
    return -1;  
} 

int main() { 
    int a, b, c; 
    const int sum = 1000; 
    int result = findit(sum, &a, &b, &c); 
    if (result == -1) { 
     std::cout << "No result!" << std::endl; 
     return 1; 
    } 
    std::cout << "a:" << a << std::endl; 
    std::cout << "b:" << b << std::endl; 
    std::cout << "c:" << c << std::endl; 
    std::cout <<"Result:" << result << std::endl; 
    return 0; 
} 
+21

+1: se è abbastanza complesso da considerare un goto, è abbastanza complesso da incapsulare in una funzione ed evitare il goto. –

+2

Sì. Non complicare i loop. Basta uscire. Soluzione perfetta Ma potresti passare a, b, c per riferimento. Quindi non hai bisogno di scherzare con i puntatori. –

+0

Grazie, questa è una bella soluzione. Non rende il codice meno leggibile. – Lucas

4

Non riesco a pensare ad un'alternativa migliore. Ma un'alternativa non utilizza goto sarebbe modificando il primo for -loop:

for (a = 1; a<sum && result == -1; a++){ 

Poi break dalla seconda for -loop. Funzionerà presupponendo che il risultato non sarà mai -1 dopo il secondo for -loop è stato interrotto da break.

+2

Non vedo questo come meglio. Stai rendendo più difficile leggere il codice mentre aggiungi ulteriori condizioni al test. –

+0

E hai ragione. Come ho detto, non riuscivo a pensare ad un'alternativa migliore, ho solo mostrato un modo per evitare il ciclo goto senza alterare considerevolmente il codice. – Blixt

+0

Vorrei aggiungere che la soluzione di Alex Martelli è ovviamente migliore. Incapsularlo in una funzione è probabilmente la soluzione migliore in questo caso. – Blixt

4

Si potrebbe dichiarare una bool found = false in alto e quindi aggiungere && !found al vostro per ciclo condizionali (dopo a < sum e b < sum) e quindi impostare risultata vera in cui il vostro goto corrente è. Quindi rendi il tuo output condizionale su true.

3

Ho appena trovato questo nella barra laterale "correlati". Un thread interessante nel complesso, ma in particolare, this è una risposta alla mia domanda.

6

Vedere this question su rompere di 2 anelli. Ci sono risposte molto migliori fornite che usare un goto.

La risposta migliore è quella di inserire il secondo ciclo in una funzione e chiamare tale funzione dall'interno del primo ciclo.

codice copiato dalla risposta di mquander

public bool CheckWhatever(int whateverIndex) 
{ 
    for(int j = 0; j < height; j++) 
    { 
     if(whatever[whateverIndex][j]) return false; 
    } 

    return true; 
} 

public void DoubleLoop() 
{ 
    for(int i = 0; i < width; i++) 
    { 
     if(!CheckWhatever(i)) break; 
    } 
} 

Anche se ritengo che utilizzando un goto in questo caso non è così male come uccidere gattini. Ma è vicino.

+10

Come ci siamo tutti lavati il ​​cervello? :) La struttura risultante di questo codice è più "complessa" rispetto all'utilizzo di goto (a causa dell'ulteriore "istruzione if" in DoubleLoop). La suddivisione del codice può anche ridurre l'insieme di ottimizzazioni che un compilatore può effettuare - ad esempio una variabile locale a "CheckWhatever" potenzialmente potrebbe essere stata ottimizzata per essere al di fuori del ciclo di chiusura. –

+0

La versione goto è molto più semplice. – jv110

18

A mio parere è bene usare goto in una situazione come questa.

Btw, la predicazione accondiscendente nei confronti di avanzamento di solito proviene da persone che semplicemente pappagallo quello che hanno sentito gli altri dicono o letto da qualche parte ..

+1

Java ha denominato loop, che sono praticamente identici a questo – Casebash

+3

@Casebash: sicuro. Ma come è correlato alla risposta o alla domanda? –

1
int a,b,c,sum = 1000; 
for (a = 1; a<sum; ++a) 
for (b = 1; b<sum; ++b){ 
    c = sum-a-b; 
    if (a*a+b*b == c*c) sum = -a*b*c; 
} 
printf("a: %d\n",a-1); 
printf("b: %d\n",b-1); 
printf("c: %d\n",c); 
printf("Result: %d\n",-sum); 

risultato ottimizzato anche fuori ..: P

Comunque amo andare a S!