2009-06-21 8 views
19

Resuelvo el problema 9 en el Project Euler. En mi solución, uso una declaración "goto" para salir de dos bucles for. El problema es el siguiente:¿Está utilizando goto una forma legítima de romper dos bucles?

un triplete de Pitágoras es un conjunto de tres números naturales, por abc que,

a^2 + b^2 = c^2

Por ejemplo, 3^2 + 4^2 = 9 + 16 = 25 = 52.

Existe exactamente un triplete pitagórico para el cual a + b + c = 1000. Encuentre el producto abc.

Mi solución es en 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; 

Dado que las declaraciones "Ir a" no son muy populares entre los programadores de C++, me gustaría saber, si esto podría ser considerado un uso razonable de "Goto" . O si hay una solución mejor para el problema que no necesita "goto". Con esto no me refiero a una solución que simplemente evite "goto", pero que evite "goto" de una manera que mejore el algoritmo.

+13

me encantan las pestañas de 8 espacios que estás usando .... muy sexy. –

+0

gracias, me gustan también :-) – Lucas

+10

Con una pestaña de 16 espacios en el medio ... –

Respuesta

45

return es un "estructurado" goto que muchos programadores encuentran más aceptable! Por lo tanto:

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: si es lo suficientemente complejo como para considerar un goto, es lo suficientemente complejo como para encapsular en una función y evitar el goto. –

+2

Sí. No compliques los bucles. Solo sal. Solución perfecta. Pero podrías pasar a, b, c por referencia. Entonces no necesitas perder el tiempo con los punteros. –

+0

Gracias, esta es una buena solución. No hace que el código sea menos legible. – Lucas

4

No puedo pensar en una mejor alternativa. Pero una alternativa no usando goto sería modificar el primer for -loop:

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

Entonces break fuera de la segunda for -loop. Eso funcionará suponiendo que el resultado nunca será -1 después de que el segundo for -loop se haya roto por break.

+2

No veo esto como mejor. Está haciendo que el código sea más difícil de leer ya que está agregando más condiciones a la prueba. –

+0

Y tienes razón. Como dije, no pude pensar en una mejor alternativa, solo mostré una forma de evitar el bucle goto sin alterar el código considerablemente. – Blixt

+0

Me gustaría agregar que la solución de Alex Martelli es, por supuesto, mejor. Encapsularlo en una función es probablemente la mejor solución en este caso. – Blixt

4

Se puede declarar una bool found = false en la parte superior y luego añadir a sus && !found de bucle condicionales (después a < sum y b < sum) y luego ponga encontrado que cierto cuando su Goto actual. Luego haz que tu salida sea condicional en que found sea verdadero.

3

Acabo de encontrar esto en la barra lateral "Relacionada". Un hilo interesante en general, pero en particular, this es una respuesta a mi pregunta.

6

Ver this question sobre la ruptura de 2 bucles. Se proporcionan respuestas mucho mejores que usar un goto.

La mejor respuesta es colocar el segundo ciclo en una función y llamar a esa función desde adentro de su primer ciclo.

código copiado de la respuesta de 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; 
    } 
} 

Aunque siento que el uso de un Goto, en este caso no es tan malo como matar gatitos. Pero está cerca.

+10

¿Cómo nos han lavado todos los cerebros? :) La estructura resultante de este código es más "compleja" que usar goto (debido a la 'instrucción if' adicional en DoubleLoop). Dividir el código también puede reducir el conjunto de optimizaciones que un compilador puede hacer; por ejemplo, una variable que es local para "Comprobar lo que sea" potencialmente podría haberse optimizado para estar fuera del bucle adjunto. –

+0

La versión goto es mucho más simple. – jv110

18

En mi opinión, es muy bien utilizar goto en una situación como esta.

Por cierto, la predicación condescendiente contra Goto lo general proviene de personas que sólo como loros lo que escucharon otros dicen o leído en alguna parte ..

+1

Java ha nombrado loops, que son bastante idénticos a este – Casebash

+3

@Casebash: Claro. ¿Pero cómo se relaciona esto con la respuesta o la pregunta? –

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); 

resultado también optimizado a cabo ..: P

todos modos, me encanta ¡ve a S!

Cuestiones relacionadas