Y es que hay que decir que en el mundo de la programación, hay una palabra maldita que se desancoseja usar pase lo que pase; sabéis cuál es, ¿verdad? Sí… el tan temible y odiado GOTO
Ciertamente lo de poder saltar a cualquier parte del código de una manera directa es una cosa la mar de peliaguado, pero voy a tener que decir que últimamente le he visto su utilidad… sobre todo si os toca picar en C y no tenéis excepciones ni nada parecido.
Imaginar un mundo sin excepciones y en el cuál tenéis funciones la mar de majas que tienen muchos if’s internos porque están comprobando un montón de posibilidades que pueden fallar y, por cada uno de estos fallos, no se puede continuar con el flujo de la función y hay que poner un mensaje de error y salir.
Si no fuera mucho, pues sí, se pueden anidar los if’s y ser feliz… Pero si hablamos de 20, por ejemplo, nos quedamos tabulando como posesos todo el día… Así que ahí tenemos un problema porque si hacemos ésto, tendremos un código ilegible y totalmente dependiente de la barra de scroll.
Me podríais decir que una buena opción sería la de ir poniendo return en cada if inválido y así romper el flujo de la función y volver a la función llamante. Pues sí, se puede hacer, pero por un lado tenemos que poner múltiples puntos de salida de una función no es nada recomendable (y menos si es una función grande en tamaño) y por otra parte imaginaos que tengamos que liberar memoria o algún último tipo de operación (vamos, lo que iría en un catch de toda la vida).
Pues en este punto, la mejor opción es tirar de goto… y tener algo como ésto:
<br>void<br>GotoRevenge (void)<br>{<br> if (!cond1l) {<br> nRet = -1;<br> goto exit;<br> }<br> xxxxxxxxxxxxxxxxxxxxxx<br><br> if (!cond2) {<br> nRet = -2;<br> goto exit;<br> }<br> xxxxxxxxxxxxxxxxxxxxxx<br><br> if (!cond3) {<br> nRet = -3;<br> goto exit;<br> }<br> xxxxxxxxxxxxxxxxxxxxxx<br><br> if (!cond4) {<br> nRet = -4;<br> goto exit;<br> }<br> xxxxxxxxxxxxxxxxxxxxxx<br><br> exit:<br> LiberarMemoria();<br> /* Y todo tipo de operaciones de salida */<br> return nRet;<br>}<br>
Si hubiera ido poniendo en cada else la liberación de memoria (por ejemplo), hubiéramos tenido una función totalmente horrible, tanto en tamaño (repitiendo código sin parar) y tabulado al máximo.
¡No marginemos al goto! :P
Drizzt says:
Juas, seguramente la JVM o el compilador de C++ traducen esas capturas de excepciones a gotos…
Con lo bonicos que son esos FAR JUMP y ese IRET “mágico” en assembler para saltar a zonas de código que no estaban en tu segmento CS…
Hypnotoad says:
Cuando vi que ibas a defender al goto, se me quedaron los ojos como platos, me presigne y empece a recitar versiculos de la biblia para conjurar esa palabra maldita surgida de las profundidades del necronomicon.
Pero habiendo visto el ejemplo, he de reconocer que si haces saltos dentro de la funcion y de la manera que has descrito, hasta puede ser justificable.
De todas formas ¿poniendo un else a cada if con un break no lograrias romper el flujo de la funcion y ejecutar las sentencias comunes?:
#
void
#
GotoRevenge (void)
#
{
#
if (!cond1l) {
#
nRet = -1;
}
else
break;
#
xxxxxxxxxxxxxxxxxxxxxx
#
#
if (!cond2) {
#
if (!cond4) {
if (!cond5) {
nRet = -2;
}
else
break;
}
else
break;
}
else
break;
#
xxxxxxxxxxxxxxxxxxxxxx
#
#
if (!cond3) {
#
nRet = -3;
}
else
break;
#
xxxxxxxxxxxxxxxxxxxxxx
#
LiberarMemoria();
#
/* Y todo tipo de operaciones de salida */
#
return BadCode;
Y asi volvemos a estigmatizar el goto!!!!! XD
Hail to the Hypnotoad!
Hypnotoad says:
Aaaargh!
No me ha cogido las tabulaciones! Ahora no se ve un pimiento!
Drizzt says:
Jeje, los break son una forma de goto encubierto, asi que segun Dijkstra son otro avatar de un culo de mal asiento.
De todas formas los break solo funcionan dentro de bucles -creo-, por lo que habría que modificar levemente el código de Hipnotoad de tal forma que:
/////////////////////////
int oneWayTicket=1;
while (oneWayTicket){
oneWayTicket=0;
if (!cond1l) {
#
nRet = -1;
}
else
break;
[…]
}//end while
LiberarMemoria();
#
/* Y todo tipo de operaciones de salida */
#
return BadCode;
/////////////////////////
Y lo tendriamos son gotos…
Juas… ya programé mucho por hoy XDDD
Disquete Enmascarado says:
Si lo divides en dos funciones/métodos no hace falta ni goto ni break.
Una para los if y dar valor a la variable de retorno y otro para las otras operaciones.
¡Muerte al Goto!
int checkCondition (void)
{
nRet=0;
if (!cond1l) {
nRet = -1;
}
//xxxxxxxxxxxxxxxxxxxxxx
if (!cond2) {
nRet = -2;
}
return nRet;
}
int gotSucks(void)
{
int nRet = checkCondition();
if(nRet!=0) {
LiberarMemoria();
/* Y todo tipo de operaciones de salida */
}
return nRet;
}
Black Hole says:
1. Esos descontroles tan emocionantes… ¿es que nadie echa de menos tener una pila con datos aleatorios? xD
2. 3. Vaya, los tabs no van bien por estos lares… La idea esta bien, pero me parece que los breaks solo van con los bucles…
4. Ale, a descansar ;)
5. Esa es otra de las soluciones propuestas pero implica que tienes que tener mil millones de funciones para cada cosa y, si encima cada condición depende de un contexto general de la función pero que se va calculando dentro del cuerpo puede ser un poco… lioso ;P
Disquete Enmascarado says:
Nadie dijo que la programación funcional sea fácil ;)
sleepwalker says:
Jajaj, el primer día en la facultad nos hablaron del goto como si fuese Lord Voldemort en Harry Potter O_O no se podía ni nombrar xD
Disquete Enmascarado says:
¡El único Goto bueno es este!: http://en.wikipedia.org/wiki/Goto_Dengo
Black Hole says:
7. Ciertamente lo repito :P
8. Jajajajja, es una buena comparación pero sí, en la práctica es tratado así O_O
9. Alaaaa, ¡¡qué cosas conoces!!