======= Estructuras de control selectivas =======
Hasta ahora, la lista de instrucciones de nuestro programa que se ejecutarán está **fija**: Esto es, siempre especificamos una lista de instrucciones, y **cada una de ellas** es ejecutada en orden, de arriba hacia abajo.
Hay veces en las que solamente queremos llevar a cabo una acción determinada **a veces**, cuando **se cumple una cierta condición** particular que hace que queramos llevar a cabo la tarea. Veremos en esta lección cómo se puede lograr esto en C++.
===== La instrucción if =====
La instrucción ''if'' sirve para instruir a la computadora a que lleve a cabo un determinado conjunto de instrucciones, **únicamente cuando se cumpla una condición específica**.
==== Versión común ====
La sintaxis (forma de escritura) de la instrucción if es la siguiente:
if (condicion)
{
instruccion1;
instruccion2;
//...
instruccionFinal;
}
Notar que los paréntesis alrededor de la condición **son obligatorios**. De manera similar a lo que ocurre en main, donde escribimos todas las instrucciones que queremos que se ejecuten, las llaves ''{'' y ''}'' delimitan un //bloque// de instrucciones, que únicamente se ejecutarán cuando se cumpla la condición. Si la condición no se cumple, se saltearán **todas las instrucciones encerradas entre llaves**.
Por ejemplo, el siguiente programa puede utilizarse para leer un número, y escribir en pantalla un mensaje de acuerdo a su signo:
#include
using namespace std;
int main()
{
int x;
cin >> x;
if (x > 0)
{
cout << "El numero " << x << " es positivo." << endl;
}
if (x < 0)
{
cout << "El numero " << x << " es negativo." << endl;
}
return 0;
}
Notar algo muy importante, que es que **indentamos** (colocamos espacios a la izquierda de) las instrucciones del if que encerramos entre llaves: Esto lo hacemos por claridad, para que sea mucho más fácil leer los programas. Cada vez que ponemos un bloque de instrucciones entre llaves, es conveniente que todas las instrucciones contenidas estén más a la derecha visualmente que las instrucciones que rodean al bloque, para que sea más fácil entender a simple vista dónde comienza y termina el bloque de instrucciones del if. Notar que al compilador no le importan los espacios, y únicamente se basa en las **llaves** para decidir hasta dónde llega el if. Pero es importante para poder leer y entender más fácil el código, utilizar prolijamente los espacios.
Vemos aquí nuestro primer ejemplo de **condición**: ''x > 0'' es una condición que puede colocarse en un if (entre paréntesis), y que se cumple justamente cuando la expresión ''x'' es mayor que cero. Como ''x'' es directamente una variable con el valor que leímos, esto se cumplirá cuando el número ingresado por el usuario sea mayor que cero. En dicho caso (¡y solo en dicho caso!) el programa ejecutará **las instrucciones entre llaves** que siguen al if. En este caso, es una única instrucción que muestra un mensaje indicando que el número es positivo.
Similarmente, **luego** de verificar la primera condición y (quizás) ejecutar lo indicado entre llaves, se llega en el programa al segundo if: En este se verifica la condición ''x < 0'', y por lo tanto las instrucciones entre llaves que siguen a este if únicamente serán ejecutadas cuando el número ingresado sea negativo.
Por ejemplo, si ingresamos el número 4 veremos en pantalla al ejecutar la siguiente interacción:
4
El numero 4 es positivo.
------------------
(program exited with code: 0)
Press return to continue
En cambio, si ingresamos un valor de -2, al ejecutar veremos en pantalla lo siguiente:
-2
El numero -2 es negativo.
------------------
(program exited with code: 0)
Press return to continue
Si ingresamos un valor 0, veremos lo siguiente:
0
------------------
(program exited with code: 0)
Press return to continue
¡Vemos en este caso que el programa no imprime ningún mensaje! ¿Por qué ocurre esto?
La computadora ejecuta las instrucciones indicadas una por una en orden. En particular, cuando llega a cada if, verifica la condición correspondiente para ver si se cumple, y solo ejecuta la instrucción entre llaves (de impresión del mensaje) cuando la condición se cumple. Por lo tanto si este programa no imprime nada, debería ser porque cuando se ingresa el valor 0, ninguna de las dos condiciones se cumple, y no se ejecuta ninguno de los ifs.
En efecto, cero es un número especial, el único entero que no es ni positivo ni negativo, y por lo tanto ''0 < 0'' y ''0 > 0'' son ambas **falsas**. Podemos entonces agregar este caso como un nuevo if en el programa:
#include
using namespace std;
int main()
{
int x;
cin >> x;
if (x > 0)
{
cout << "El numero " << x << " es positivo." << endl;
}
if (x < 0)
{
cout << "El numero " << x << " es negativo." << endl;
}
if (x == 0)
{
cout << "El numero ingresado es cero." << endl;
}
return 0;
}
Si ejecutamos ahora los mismos ejemplos de antes, obtendremos los mismos resultados en los primeros dos casos: pero al ingresar el valor cero, ahora observaremos lo siguiente:
0
El numero ingresado es cero.
------------------
(program exited with code: 0)
Press return to continue
Notar que en nuestra tercera condición, hemos utilizado por primera vez el operador ''=='': Este operador no tiene **nada** que ver con el ''='': Recordemos que el operador ''='' se utiliza para la **asignación** de variables ("meter valores en cajas"). El ''=='' en cambio se utiliza para **expresar condiciones**, y funciona como la **igualdad matemática**.
El ''=='' es un ejemplo de los que se denominan //operadores de comparación//. Los veremos en más detalle muy pronto.
Veamos un segundo ejemplo de programa, que muestre un mensaje de acuerdo a la paridad del número ingresado:
int main()
{
int x;
cin >> x;
if (x % 2 == 0)
{
cout << "El numero " << x << " es par." << endl;
}
if (x % 2 == 1)
{
cout << "El numero " << x << " es impar." << endl;
}
return 0;
}
Recordemos que ''%'' es el operador de "resto de la división", también llamado //módulo//. Para saber si un número es par o impar, debemos considerar el **resto de la división por 2**. Cuando el resto sea 0, el número es par, y cuando sea 1, es impar. Esas son las condiciones que verificamos en los ifs de este programa, y en cada caso, imprimimos un mensaje apropiado para la condición que se cumple.
==== El else ====
En el último ejemplo, verificábamos si un número era par o impar, e imprimíamos un mensaje de acuerdo a la paridad. Notemos que en este caso tenemos **dos opciones excluyentes**: O bien el número es par, y entonces **solamente se imprime el mensaje para el caso par**, o bien esto no ocurre(porque el número es impar), y entonces **solamente se imprime el mensaje para el caso impar**.
Esta situación, en la cual hay una cierta **condición**, y se debe ejecutar un conjunto de instrucciones **cuando se cumple** la condición, y otro conjunto **cuando no se cumple**, es muy común. Para ello existe una parte opcional de la instrucción if, que hasta ahora no hemos utilizado, y es la sección ''else''.
Es posible escribir lo siguiente:
if (condicion)
{
instruccionA1;
instruccionA2;
instruccionA3;
...
}
else
{
instruccionB1;
instruccionB2;
instruccionB3;
...
}
En este caso, el primer bloque de instrucciones (instruccionA1, instruccionA2, etc) corresponde al "if normal", y se ejecutará cuando la condición indicada entre paréntesis ocurra. En cambio, el segundo bloque de instrucciones (instruccionB1, instruccionB2, etc) se pone a continuación de ''else'', y se ejecuta **cuando la condición del if no ocurre**. De esta forma, uno solo de los bloques de instrucciones será ejecutado, dependiendo de si la condición del if vale o no.
A continuación mostramos el ejemplo anterior de par o impar, reescrito utilizando la instrucción else:
int main()
{
int x;
cin >> x;
if (x % 2 == 0)
{
cout << "El numero " << x << " es par." << endl;
}
else
{
cout << "El numero " << x << " es impar." << endl;
}
return 0;
}
En este caso, en lugar de utilizar un segundo ''if'' con la condición (x % 2 == 1), como esta condición es la que ocurre exactamente cuando no ocurre la primera, podemos simplemente extender el primer ''if'' con un ''else'', para indicar qué hacer cuando el número **no es par** (en cuyo caso, será impar).
==== Operadores de comparación ====
Para expresar las condiciones anteriores, hemos utilizado los operadores <, >, e ==. Estos operadores se utilizan para expresar **condiciones**, mediante la **comparación** de otros dos valores. Así, ''x > 10'' expresa la condición de que el valor almacenado en ''x'' debe ser **mayor** que 10. Similarmente, ''x + y == z'' expresa la condición de que el valor almacenado en ''x'', más el valor almacenado en ''y'', debe ser **igual** al valor almacenado en ''z''.
Estos operadores se llaman //operadores de comparación//. A continuación mostramos los más importantes operadores de comparación, junto a un texto que indica su significado:
== "Igual a"
!= "Distinto de"
< "Menor a"
> "Mayor a"
<= "Menor o igual a"
>= "Mayor o igual a"
Cada uno de estos operadores puede utilizarte para comparar los valores de dos expresiones, obteniéndose así una **condición** que puede utilizarse en un if. Recordar siempre que el operador de comparación "==", y el operador de asignación "=" son completamente diferentes, y mezclarlos puede llevar a errores en el comportamiento del programa.
Ya hemos usado estos operadores con valores de tipo int: en dicho caso, la comparación se realiza por valor numérico. También es posible utilizar los operadores de comparación con variables string (textos): En este caso, las palabras se ordenan en el orden del diccionario. El nombre técnico para el orden del diccionario (Donde primero van las palabras con A, luego con B, luego con C, etc) es "orden lexicográfico". Por ejemplo, tendremos que "vaca" > "sopa", y "abcd" < "bcda". Notar sin embargo que como ya mencionamos, a cada caracter (char) corresponde un valor numérico ASCII, y las mayúsculas y minúsculas tienen valores distintos. Como C++ ordena las variables de tipo string usando estos códigos, cadenas que mezclen mayúsculas y minúsculas no se ordenarán según el orden normal del diccionario, ya que en ASCII **todas las mayúsculas vienen antes que todas las minúsculas**. Así por ejemplo, si bien "burro" > "agua", tenemos que "Burro" < "agua", pues la 'B' viene antes que la 'a', que viene antes que la 'b'.
==== Versión con una única instrucción ====
Hemos visto que la sintaxis (escritura) completa del ''if'' tiene la siguiente forma:
if (condicion)
{
instrucciones;
}
else
{
instrucciones;
}
Además, ya hemos mencionado que la parte del ''else'', para especificar qué hacer cuando **no** se cumple la condición, es **opcional**. Una opción adicional que existe en C++ en el caso del ''if'' (o el ''else''), es la de no utilizar las llaves cuando el bloque de instrucciones que delimitan contiene una sola instrucción. En este caso, si no usamos llaves, C++ asumirá que **la primera** instrucción que sigue a continuación conforma el bloque completo.
Veamos algunos ejemplos:
// Escritura completa
if (x > 0)
{
cout << "El numero es positivo" << endl;
}
cout << "Fin del programa" << endl;
// Escritura sin llaves
if (x > 0)
cout << "El numero es positivo" << endl;
cout << "Fin del programa" << endl;
Los dos ejemplos anteriores son equivalentes, ya que hay una sola instrucción entre llaves. Notemos en cambio que los siguientes dos ejemplos **no** son equivalentes:
// Escritura completa
if (x > 0)
{
cout << "El numero ";
cout << "es positivo" << endl;
}
cout << "Fin del programa" << endl;
// Escritura sin llaves:
// CAMBIA EL SIGNIFICADO!
// Las llaves anteriores NO SE PUEDEN OMITIR.
if (x > 0)
cout << "El numero ";
cout << "es positivo" << endl;
cout << "Fin del programa" << endl;
// La version anterior sin llaves equivale a esto:
if (x > 0)
{
cout << "El numero ";
}
cout << "es positivo" << endl;
cout << "Fin del programa" << endl;
En el ejemplo anterior, vemos que omitir las llaves cuando el bloque de instrucciones que queremos ejecutar tiene más de una instrucción es un error. Solamente es posible omitir las llaves, cuando el bloque tiene **una única instrucción**. **Ante la duda**, o posibilidad de confusión, es mejor dejar las llaves aunque se utilice una única instrucción, para evitar problemas.
Existe un peligro más cuando omitimos las llaves, y este peligro es el resultado de que en C++ la parte ''else'' sea opcional. Supongamos un código como el siguiente:
// Ejemplo 1 (resulta correcto)
if (x > 0)
if (x % 2 == 0)
cout << "Positivo par" << endl;
else
cout << "Positivo impar" << endl;
// Ejemplo 2 (resulta incorrecto)
if (x > 0)
if (x % 2 == 0)
cout << "Positivo par" << endl;
else
cout << "Negativo" << endl;
Notemos que, más allá de los mensajes que se van a mostrar, la única diferencia entre el primer y el segundo ejemplo es la indentación (cantidad de espacios a la izquierda) del ''else''. Pero **al compilador no le importa la cantidad de espacios**. Por lo tanto, ambos casos son interpretados de idéntica manera. En este caso... ¿Corresponde para C++ el ''else'' al primer ''if'', como querríamos en el ejemplo2, o corresponde al segundo, como querríamos en el ejemplo 1?
La regla que sigue C++, que determina cómo resolver esta confusión cuando no se usan llaves en el ''if'', es que un ''else'' en el código corresponde al ''if'' inmediatamente anterior. Es decir, el compilador interpreta la situación como querríamos en el ejemplo 1, con lo cual el ejemplo 2 nos resulta incorrecto. Para escribir lo que querríamos en el ejemplo 2, es necesario sí o sí utilizar llaves.
Veamos a continuación entonces cómo escribir los ejemplos con la escritura completa con llaves:
// Ejemplo 1 (con llaves, correcto)
if (x > 0)
{
if (x % 2 == 0)
{
cout << "Positivo par" << endl;
}
else
{
cout << "Positivo impar" << endl;
}
}
// Ejemplo 2 (con llaves, correcto)
if (x > 0)
{
if (x % 2 == 0)
{
cout << "Positivo par" << endl;
}
}
else
{
cout << "Negativo" << endl;
}
Notar que ahora en el ejemplo 2, al tener todas las llaves, el compilador no puede confundirse y emparejar el ''else'' con el ''if'' interno, ya que ese ''if'' se encuentra **dentro** del bloque de instrucciones entre llaves, y por lo tanto no puede corresponderse con un ''else'' que está por fuera de las llaves.
===== Ejercicios =====
* [[http://juez.oia.unsam.edu.ar/#/task/multi_tres/statement|Escribir un programa que lea un numero, y nos diga si es múltiplo de 3 o no.]]
* [[http://juez.oia.unsam.edu.ar/#/task/cual_va_primero/statement|Escribir un programa que lea dos palabras, y nos diga cuál viene primero en el diccionario.]]
* [[http://juez.oia.unsam.edu.ar/#/task/es_bisiesto/statement|Escribir un programa que lea un número de año, y nos diga si es bisiesto.]]
===== Operadores lógicos =====
A veces, queremos expresar **condiciones compuestas**, en base a otras condiciones más simples. Los //operadores lógicos// sirven para obtener tales condiciones. A continuación mostramos los operadores lógicos más comunes, junto con su nombre:
&& "and"
|| "or"
! "not"
El operador ''&&'', denominado "and", se utiliza para formar una condición compuesta en la cual se exige que otras **dos** condiciones **se cumplan al mismo tiempo**. Por ejemplo, ''x > 0 && x % 2 == 0'' expresa la condición de que x sea un número par positivo, indicando que deben cumplirse tanto ''x > 0'' como ''x % 2 == 0''. ''x > 0 && x < 0'', por ejemplo, denota una condición imposible de cumplir, ya que se pide que x sea positivo y negativo.
El operador ''||'', denominado "or", se utiliza para formar una condición compuesta en la cual se exige que **al menos una** de otras **dos** condiciones se cumpla. Así por ejemplo, cero será el único número que no cumple la condición ''x > 0 || x < 0'': En esta condición se pide que x sea positivo o negativo. El único número que no cumple ninguna de ellas es el cero. Otro ejemplo es ''x % 2 == 0 || x % 3 == 0'', en el cual se pide que x sea múltiplo de 2 o de 3. Además, si x resulta ser múltiplo de ambos (como por ejemplo, 12), la condición igualmente se cumple, pues con una sola que se cumpla basta, y si se cumplen las dos "mejor".
Finalmente, el operador ! se usa para invertir una condición dada, que generalmente deberá encerrarse entre paréntesis. Por ejemplo, ''!(x < 0)'' es completamente equivalente a ''(x >= 0)''. Por otro lado, ''!(x % 2 == 0 || x % 3 == 0)'', por ejemplo, estaría negando la condición anterior de que el número x sea múltiplo de 2 o de 3, y por lo tanto esta condición solamente será cierta cuando el número no sea múltiplo ni de 2 ni de 3.
Utilizando estos operadores lógicos, podemos muchas veces resumir largas cadenas de ifs, en una sola condición compuesta más clara. Veamos por ejemplo el siguiente ejemplo para decidir si un año es bisiesto:
#include
using namespace std;
int main()
{
int year;
cin >> year;
if (year % 400 == 0)
cout << "Es bisiesto." << endl;
else
{
// En este caso, no es multiplo de 400
if (year % 100 == 0)
cout << "No es bisiesto " << endl;
else
{
// En este caso, no es multiplo de 100
if (year % 4 == 0)
cout << "Es bisiesto." << endl;
else
cout << "No es bisiesto." << endl;
}
}
return 0;
}
Si bien es correcto, este código puede resumirse en el siguiente mucho más claro, utilizando expresiones lógicas:
#include
using namespace std;
int main()
{
int year;
cin >> year;
if (year % 400 == 0 || (year % 4 == 0 && year % 100 != 0))
cout << "Es bisiesto." << endl;
else
cout << "No es bisiesto " << endl;
return 0;
}
Notar que encerramos entre paréntesis la condición ''(year % 4 == 0 && year % 100 != 0)''. Siempre es conveniente utilizar paréntesis para indicar el orden de las operaciones lógicas, cuando encadenamos una mezcla de operaciones ''||'' y ''&&''.
===== El tipo bool =====
Ya hemos visto los tipos ''int'', ''string'' y ''char''. Veremos ahora un tipo de datos adicional: El tipo ''bool''.
Un ''bool'' representa el **resultado** de analizar si una condición es **cierta o falsa**. Por lo tanto, un valor ''bool'' representa un sí o un no. En C++, el sí se escribe ''true'' (del inglés "verdadero") y el no se escribe ''false'' (del inglés, "falso").
Ahora podemos entender que todos los operadores de comparación que vimos, así como todos los operadores lógicos, operan con expresiones y producen resultados de tipo ''bool'': ''true'' cuando la condición analizada es cierta, y ''false'' cuando la condición analizada no lo es.
Por ejemplo, en el caso de los operadores de comparación, ''1 < 2'' da por resultado ''true'', que es un valor de tipo ''bool''. ''1 == 2'' es otro valor de tipo ''bool'', que será ''false''. Como con cualquier otro tipo de datos, podemos declarar variables ''bool'':
bool cond1 = 1 == 2;
bool cond2 = 1 < 2;
if (cond1)
cout << "Esto no se ejecuta" << endl;
if (cond2)
cout << "Esto si se ejecuta" << endl;
if (cond1 || cond2)
cout << "Esto si se ejecuta" << endl;
if (cond1 && cond2)
cout << "Esto no se ejecuta" << endl;
En este ejemplo, vemos que las operaciones lógicas ''||'' y ''&&'' operan con 2 valores de tipo bool: ''||'' da por resultado ''true'' cuando al menos uno de los operandos lo es, y ''&&'' devuelve ''true'' solamente cuando ambos operandos lo son.
Además, en este ejemplo vemos que lo que hemos llamado ''condición'', y que se debe colocar entre paréntesis en el ''if'', en realidad puede ser cualquier **expresión de tipo bool**. Esto permite guardar valores **bool** intermedios en variables, y usarlos libremente en expresiones compuestas mediante operadores lógicos y de comparación.
===== Más ejercicios =====
* [[http://juez.oia.unsam.edu.ar/#/task/tri_sort/statement|Escribir un programa que lea tres números, y los vuelva a imprimir pero ordenados de menor a mayor.]]
* [[http://juez.oia.unsam.edu.ar/#/task/la_hora/statement|Escribir un programa que lea una hora en formato de 24 horas (exactamente 5 caracteres) y la imprima en forma AM / PM.]] Ejemplos:
23:12 imprime 11:12 PM
10:15 imprime 10:15 AM
12:15 imprime 12:15 PM
00:15 imprime 12:15 AM
* [[http://juez.oia.unsam.edu.ar/#/task/productis/statement|Leer dos numeros de hasta 2 digitos, e imprimir una "cuenta" del producto.]] La cuenta debe estar bien alineada a derecha. Ejemplos:
Si vienen 12 y 5,
12
x 5
----
60
Si vienen 10 y 10,
10
x 10
----
100
Si vienen 50 y 60,
50
x 60
----
3000
Si vienen 0 y 99,
0
x 99
----
0