Salto incondicional break
¶
Supongamos un programa cuya introducción de datos debe interrumpirse cuando se cumpla una de las dos condiciones preestablecidas siguientes:
El número de datos introducido supera un valor máximo.
Se cumple una determinada condición, en cuyo caso abandonaremos la introducción de datos.
Veamos dos alternativas, una sin usar break
y otra utilizándolo.
Ejemplo con while
sin usar break
// Suma de los números positivos introducidos: sin break
#include <iostream>
using namespace std;
int main()
{
int num_datos;
cout << "Número máximo de datos a leer: ";
cin >> num_datos;
int i{}, dato{}, suma{}; // A cero con inicialización uniforme
while (i < num_datos && dato >= 0)
{
cout << "Introduzca un dato (negativo para finalizar): ";
cin >> dato;
if (dato >= 0)
{
suma += dato;
++i;
}
}
if (i > 0)
{
cout << "Se han introducido " << i << " datos válidos.\n";
cout << "La suma de los datos introducidos es " << suma << ".\n";
}
else
cout << "No se han introducido datos válidos.\n";
}
Edita, compila y ejecuta el código
Al final del tema anterior sugeríamos que antes de usar un determinado tipo de bucle debíamos preguntarnos sobre si conocíamos el número de iteraciones a realizar de antemano.
En este ejemplo, la respuesta podría
ser NO, si prevemos que el usuario no completará todos los posibles
valores a introducir, es decir, el bucle while
terminará al evaluarse
la expresión dato >= 0
a false
.
Sin embargo, podríamos adoptar una postura más optimista y pensar que
el usuario introducirá todos los posibles valores y, por tanto, la salida
del bucle while
será debida a la evaluación de la expresión
i < num_datos
a false
. Y en este supuesto es donde el bucle for
parece más oportuno.
Veámoslo en la siguiente versión del programa.
Ejemplo con for
usando break
// Suma de los números positivos introducidos: usando break
#include <iostream>
using namespace std;
int main()
{
int num_datos;
cout << "Número máximo de datos a leer: ";
cin >> num_datos;
int i{}, suma{}; // A cero con inicialización uniforme
for (i = 0; i < num_datos; ++i)
{
cout << "Introduzca un dato (negativo para finalizar): ";
int dato;
cin >> dato;
if (dato < 0)
break;
suma += dato;
}
if (i > 0)
{
cout << "Se han introducido " << i << " datos válidos.\n";
cout << "La suma de los datos introducidos es " << suma << ".\n";
}
else
cout << "No se han introducido datos válidos.\n";
}
Edita, compila y ejecuta el código
En este ejemplo, la sentencia break
permite contemplar el caso
en el que el usuario no complete la introducción de todos los valores,
al introducir un valor negativo.
El salto incondicional break
ha sido considerado en muchos ámbitos,
sobre todo relacionados con la docencia, una mala práctica de programación.
La realidad es que, en muchos casos, el uso de break
facilita
la legibilidad del código y, por ello, es recomendable su uso.
Vamos a ver cómo un bucle for
es lo suficientemente versátil para
permitir una tercera versión del ejemplo, sin usar break
.
Ejemplo con for
sin usar break
// Suma de los números positivos introducidos
// Con for sin usar break
#include <iostream>
using namespace std;
int main()
{
int num_datos;
cout << "Número máximo de datos a leer: ";
cin >> num_datos;
int i{}, suma{}, dato{}; // A cero con inicialización uniforme
for (i = 0; i < num_datos && dato >= 0; ++i)
{
cout << "Introduzca un dato (negativo para finalizar): ";
cin >> dato;
if (dato >= 0)
suma += dato;
else
--i; // Para compensar el ++i de fin de bloque for
}
if (i > 0)
{
cout << "Se han introducido " << i << " datos válidos.\n";
cout << "La suma de los datos introducidos es " << suma << ".\n";
}
else
cout << "No se han introducido datos válidos.\n";
}
Edita, compila y ejecuta el código
Esta versión es, sin lugar a dudas, desafortunada. Los bucles for
es preferible usarlos con una condición de salida simple. Además, como
el incremento del contador i
se produce al final del bucle, debemos decrementarlo en caso de
salida prematura por dejar de cumplirse la condición dato >= 0
.
Centinelas¶
El uso de la sentencia break
suele venir acompañada en muchos problemas del
uso de una variable centinela, también denominada testigo o bandera.
Habitualmente la variable centinela tiene un tipo booleano y nos permite discriminar cuando la finalización de un bucle se ha debido o no a un salto incondicional.
Un ejemplo clásico es la determinación de si un número es primo.
Ejemplo: determinar si un número es primo (versión 1)
// Determina si un número entero es primo. (Versión 1)
#include <iostream>
using namespace std;
int main()
{
int numero;
do
{
cout << "Deme un entero positivo mayor que 1: ";
cin >> numero;
if (numero < 1)
cout << " El valor introducido no es válido.\n";
}
while (numero < 1);
bool es_primo{true}; // Variable centinela o bandera
for (int divisor = 2; divisor < numero ; ++divisor)
{
if (numero % divisor == 0)
{
es_primo = false;
break;
}
}
cout << "El número " << numero;
if (es_primo)
cout << " es primo.\n";
else
cout << " no es primo.\n";
}
Edita, compila y ejecuta el código
Una advertencia: existen formas más eficientes de realizar la tarea propuesta; el código anterior debe verse como un intento inicial.
La estrategia consiste en determinar, mediante un bucle, todos los posibles
divisores legítimos, rango [2, numero-1]
, que harían que se pudiera
decidir que el número no es primo.
Nótese que el bucle tiene una especie de carácter asimétrico:
para concluir que el número es primo, se debe llevar el bucle hasta su conclusión, investigando todos los posibles divisores, sin hallar ningún divisor exacto.
para concluir que el número no es primo, basta con encontrar el primer divisor exacto.
La variable centinela es_primo
es la encargada de poner de
manifiesto cuál de las dos situaciones se ha producido.
Por supuesto, el lenguaje nos brinda otras opciones para programar
el problema anterior sin usar break
. Como vimos anteriormente,
basta incorporar la condición de activación de la salida incondicional
a la expresión de la condición del for
.
Ejemplo: determinar si un número es primo (versión 2)
// Determina si un número entero es primo. (Versión 2)
// Sin usar break
#include <iostream>
using namespace std;
int main()
{
int numero;
do
{
cout << "Deme un entero positivo mayor que 1: ";
cin >> numero;
if (numero < 1)
cout << " El valor introducido no es válido.\n";
}
while (numero < 1);
bool es_primo{true}; // Variable centinela o bandera
for (int divisor = 2; divisor < numero && es_primo; ++divisor)
if (numero % divisor == 0)
es_primo = false;
cout << "El número " << numero;
if (es_primo)
cout << " es primo.\n";
else
cout << " no es primo.\n";
}
Edita, compila y ejecuta el código
Dejamos al alumno transformar este ejemplo usando while
en lugar
de for
.
En los ejemplos anteriores hemos utilizado una variable ad hoc para el centinela. En muchos problemas no es estrictamente necesario utilizarlas. Sin embargo, su uso suele mejorar la legibilidad del código.
Así, el ejemplo de la determinación de si un número es primo permite usar
la variable divisor
como centinela.
Ejemplo: determinar si un número es primo (versión 3)
// Determina si un número entero es primo. (Versión 3)
// Sin usar centinela explícito
#include <iostream>
using namespace std;
int main()
{
int numero;
do
{
cout << "Deme un entero positivo mayor que 1: ";
cin >> numero;
if (numero < 1)
cout << " El valor introducido no es válido.\n";
}
while (numero < 1);
int divisor{2};
for (; divisor < numero; ++divisor)
if (numero % divisor == 0)
break;
cout << "El número " << numero;
if (divisor == numero)
cout << " es primo.\n";
else
cout << " no es primo.\n";
}
Edita, compila y ejecuta el código
Véase como nuestro centinela divisor
se define e inicializa fuera del
bucle y, por tanto, podemos dejar vacía esa parte del bucle for
.
Para terminar con esta panoplia de ejemplos, véase una última
implementación con while
muy compacta.
Ejemplo: determinar si un número es primo (versión 4)
// Determina si un número entero es primo. (Versión 3)
// Con while sin usar centinela explícito
#include <iostream>
using namespace std;
int main()
{
int numero;
do
{
cout << "Deme un entero positivo mayor que 1: ";
cin >> numero;
if (numero < 1)
cout << " El valor introducido no es válido.\n";
}
while (numero < 1);
int divisor{2};
while (numero % divisor)
++divisor;
cout << "El número " << numero;
if (divisor == numero)
cout << " es primo.\n";
else
cout << " no es primo.\n";
}
Edita, compila y ejecuta el código
Nótese que la salida del bucle está garantizada ya que cuando
divisor
alcanza el valor numero
, la expresión numero % divisor
se evalúa a false
.
Decidir cuál de las implementaciones vistas es superior es cuestión de debate.