Fundamentos de Informática
Departamento de Ingeniería de Sistemas y Automática. EII.
Universidad de Valladolid
T3 OPERADORES, EXPRESIONES
Y SENTENCIAS
Índice 1. Ámbito de las variables
2. Expresiones y sentencias
3. Operadores aritméticos
4. Operador de asignación
5. Operador de inicialización
6. Operadores relacionales de comparación
7. Sentencias de inicialización y asignación
8. Sentencias de Control de Flujo Condicional (1)
9. Operadores relacionales lógicos
10. Sentencias de Control de Flujo Condicional (2)
11. Sentencias de Control de Flujo Iterativo (1)
12. Operadores incremento y decremento
13. Sentencias de Control de Flujo Iterativo (2)
14. Saltos incondicionales
15. Ejemplos
2
Ámbito de las variables El ámbito o alcance en un programa de una variable es la parte del
programa donde la variable es accesible.
Básicamente podemos dividir a las variables en función del ámbito como
locales o globales.
Variables globales
Las variables globales son accesibles por todas las funciones desde su
declaración hasta el final del fichero.
El uso de variables globales no es aconsejable a pesar de que parezca útil:
• Disminuye la legibilidad
• Su uso puede producir efectos colaterales, al producirse alteraciones no
previstas de su valor en una parte del programa que afectan a su uso en
otra zona.
• Dificultan la modularidad del código.
Las variables y/o funciones globales se justifican cuando se necesitan en casi todas las funciones del programa (cin y cout, por ejemplo). Veremos más adelante una
solución de compromiso, los espacios de nombres o namespaces.
En este curso no se permitirá el uso de variables globales. 3
Ámbito de las variables Ejemplo de uso de variables globales
Los módulos de este programa (recuadros en verde) son interdependientes.
#include <iostream> using namespace std; double z=1.; //se declara z como global. void funcion(); //Estudiaremos las funciones mas adelante int main() { cout << z << endl; z=z+1.; cout << z << endl; funcion(); // funcion modifica el valor de z cout << z << endl; }
void funcion() //Estudiaremos las funciones mas adelante { z=z*z; }
4
Ámbito de las variables
Variables locales
Las variables locales son accesibles desde su declaración hasta el final
del bloque del fichero en el que han sido declaradas. Un bloque de código viene determinado por una pareja de llaves {}.
Si el bloque donde se ha declarado la variable local contiene a su vez
otros bloques, también es accesible dentro de ellos.
En caso de que una variable aparezca declarada en varios niveles de
anidamiento dentro de un bloque, prevalece la declaración del bloque más
interno.
En cualquier caso, dada su escasa legibilidad, es una situación que nunca
debería ocurrir en un programa bien diseñado.
Las variables locales se destruyen cuando la ejecución del programa
abandona el bloque donde han sido declaradas.
5
Ámbito de las variables
Ejemplo de uso de variables locales
#include <iostream> using namespace std; int main() { double z=0.; cout << z << endl; { z=z+1.; { double z=2.; cout << z << endl; }
{ cout << z << endl; } } }
6
Ámbitos
Expresiones y Sentencias
Las expresiones permiten combinar datos (variables o constantes) y operadores para calcular otros datos.
Ej:
y=a + b * 2 - 5.6;
variables
constantes literales
operadores
Un operador es un símbolo que realiza una determinada operación sobre los datos (operandos) a los que afecta.
Expresiones y Sentencias
Una sentencia es un conjunto de líneas de código
que permiten realizar una determinada acción.
(x+3)*2 // expresion
a>b && c<d // expresion
y=(x+3)*2; // sentencia
cout<<x<<endl; // sentencia
8
Operadores aritméticos
Realizan las conocidas operaciones aritméticas sobre sus operandos.
• + suma
• - resta
• / división
• * producto
• % resto de la división entera (sólo aplicable a datos enteros)
Por ejemplo: 23%5 es 3: equivale a 23-(23/5)*5
Operadores aritméticos
Cada operador genera un valor de determinado tipo a partir de los valores y tipos de los operandos.
El valor de la expresión se calcula aplicando reglas de precedencia a los operadores. Para la mayoría de los operadores se asocia de izquierda a derecha.
y=a + b * 2 - 5.6;
*
+ - de izquierda a derecha
Por último =
Operadores aritméticos
La precedencia por defecto puede ser alterada mediante el uso de paréntesis, con la interpretación habitual.
y=a + b * (2 – x); Subexpresión ()
se evalúa primero
Se pueden anidar paréntesis tanto como se necesite.
y=a * (((b +2)+3)+z);
Operadores aritméticos
C++ permite mezclar operandos de diferente tipo. Para evaluar la expresión, el lenguaje convierte automáticamente todos los datos al tipo de mayor capacidad de almacenamiento presente en la expresión:
int b=2;
float y,x=2.1;
y=2*x + b;
Las conversiones de tipo se realizan siguiendo orden de precedencia de los operadores:
2*x Constante entera 2 promovida a float (valor 2*x 4.2) 2*x+b Variable b (entera) promovida a float (valor 2*x+b 6.2)
Operadores de asignación El operador = es un operador sobrecargado: dependiendo del contexto acomete distintas operaciones. En la asignación se utiliza para modificar el valor de las variables.
…
y = y+1.5;
Antes la variable y tiene un
valor (Ej: 2.0) 20000:
20008:
y 2.0
20000:
20008:
y 3.5 Inmediatamente después
de la asignación y tiene un
valor 3.5.
El operador de asignación NO es equivalente a la igualdad matemática: una sentencia como la anterior, y = y+1.5;, no tiene sentido en matemáticas…!!!
Operadores de asignación
A la izquierda de la asignación siempre tiene que haber una variable.
A la izquierda siempre tiene que haber una variable
y = (2+3.5)/3.1+a;
y = x;
y = 5.6;
A la derecha cualquier expresión válida
2=5;
2=i+4; Error!!!
Operadores de asignación
Produce un efecto colateral (la modificación del valor de la variable a su izquierda).
Como cualquier otro operador, devuelve un valor (el del operando a su derecha).
x = a = 2; ¿Sería correcto?
Sí, equivale a:
La asignación es de los pocos operadores que se evalúan de derecha a izquierda
Al final x y a contendrán el
valor 2.
(x = (a = 2));
Vale 2
Operadores de asignación En C y C++ son muy utilizados los operadores de asignación compuestos.
•a = a OP b; puede escribirse abreviadamente como:
a OP= b ;
• a += b; /* equivale : a = a + b */
• a -= b; /* equivale : a = a - b */
• a *= b; /* equivale : a = a * b */
• a /= b; /* equivale : a = a / b */
• a %= b; /* equivale : a = a % b */
Nótese que OP= debe escribirse con los dos símbolos seguidos:
+ = +=
Operador de inicialización El símbolo = también se usa para inicializar las variables: int num=5;
No debemos confundir el operador = de asignación con el de inicialización: !hacen operaciones distintas!
int num=5; // Inicialización por copia
es distinto que
int num; // Variable sin inicializar
num=5; // Asignación del valor 5 a num
Para el caso de los tipos de datos básicos (int, double, etc.), ambas secuencias de sentencias producen el mismo resultado, pero veremos más adelante que en otros tipos de datos esto no es así.
De momento quedaros con la siguiente idea:
Se inicializa una única vez; se asigna cuantas veces sea necesario.
Operador de inicialización Para ilustrar la diferencia supongamos un problema en el cual tenemos un contenedor (disco duro) con cubos de color blanco y negro (un cubo es un bit).
Pedimos a una persona (la CPU) que construya una torre con cierto número de cubos (el tipo de dato) sobre una mesa (la memoria).
Las únicas operaciones permitidas son coger un cubo y depositarlo sobre la torre o coger el cubo superior de la torre y dejarlo en el contenedor.
Arranca el programa. La mesa está vacía. Tenemos 3 sentencias.
1ª sentencia: inicialización Formar una torre con 3 cubos blancos. El proceso consiste en buscar un cubo blanco, cogerlo, dejarlo sobre la mesa e ir formando la torre.
2ª sentencia: asignación Modificar la torre de tal forma que el cubo intermedio sea negro. El proceso en este caso consiste en quitar sucesivamente dos cubos blancos, coger un cubo negro del contenedor y dejarlo sobre la torre y coger un cubo blanco del contenedor y dejarlo sobre la torre.
3ª sentencia: inicialización Pedimos a la persona que forme una nueva torre idéntica a la existente a su lado.
Las 2 torres actualmente sobre la mesa (memoria) son iguales, pero la CPU ha realizado operaciones diferentes para realizarlas debido a que la primera se formó por asignación (ya existía una torre ahí) y la segunda por inicialización.
Operadores relacionales de comparación
Sirven para comparar dos variables o expresiones. La expresión compuesta puede resultar cierta o falsa.
• > mayor que
• >= mayor o igual que
• < menor que
• <= menor o igual que
• == igual que
• != distinto que
Si la expresión es falsa valdrá 0 (false) y si es cierta valdrá 1 (true)
(En C++ todos los valores representan verdad excepto el 0).
x=3;
y=4;
bool z=x>y; //z tomará el valor false
z=x!=y; //z tomará el valor true
Sentencias de inicialización y asignación
Las sentencias de declaración introducen nuevas variables
en el ámbito en que son declaradas. {
int x=0;
…
}
Las sentencias de asignación evalúan la expresión situada a la derecha del operador = y asignan el resultado a la
variable situada a la derecha.
x=2*y;
20
Sentencias de control de flujo
Una sentencia compuesta es el conjunto de sentencias situadas dentro de un bloque de código.
Un programa formado por sentencias de declaración, de asignación y compuestas se ejecuta de forma secuencial, una sentencia a continuación de otra sin omitir ninguna.
Las sentencias de control de flujo (condicionales e iterativas) modifican el orden natural de ejecución de un programa.
21
Sentencias de control de flujo condicional
Una estructura condicional es la que realiza un
conjunto u otro de sentencias dependiendo del
cumplimiento o no de una determinada condición.
Podemos distinguir tres tipos:
• Simples: if
• Dobles: if - else
• Múltiples: if – else if - … - else
switch
Sentencias de control de flujo condicional
Control de flujo condicional simple
Es aquella en la que se evalúa una condición y,
solo si es verdad, se ejecuta un conjunto de
sentencias asociadas.
if(condicion)
{
bloque de sentencias;
}
Las llaves { … } delimitan el conjunto de sentencias
afectadas por la condición if.
Sentencias de control de flujo condicional
Control de flujo condicional simple
¿condición?
Sentencia_1
.
.
Sentencia_n
Sí
No
Sentencias de control de flujo condicional
Control de flujo condicional simple /*----------------------------------------------------*/ /* Este programa transforma temperaturas de */ /* grados centigrados a grados kelvin */ /* Comprueba si la temperatura tiene existencia real */ /*----------------------------------------------------*/ #include <iostream> using namespace std; main() { double centig; cout<<"Introduzca grados celsius:"; cin>>centig; if(centig>=-273.) { double kelvin=centig+273.; // Inicializacion cout<<centig<<" grados celsius son "<<kelvin
<<" grados kelvin\n"; } }
Sentencias de control de flujo condicional
Control de flujo condicional simple
if(a!=0)
{
a = 1/a;
}
if(a!=0) if(a)
a=1/a; a=1/a;
a=5;
if(a=1)
printf("La variable vale 1\n");
Bloque con una sola sentencia:
las llaves pueden omitirse
Equivalentes
Error muy común:
¡se está asignando en
lugar de comparar!
Sentencias de control de flujo condicional
Control de flujo condicional doble
Permite ejecutar dos bloques de acciones distintos
dependiendo de que se cumpla la condición o no.
if(condicion)
{
bloque 1 de sentencias;
}
else
{
bloque 2 de sentencias;
}
Sentencias de control de flujo condicional
Control de flujo condicional doble
¿condición?
Sentencia_1.1
.
.
Sentencia_1.n
Sí
No
Sentencia_2.1
.
.
Sentencia_2.n
Sentencias de control de flujo condicional Control de flujo condicional doble /*----------------------------------------------------*/ /* Grados centigrados -> grados kelvin */ /* Comprueba si la temperatura tiene existencia real */ /* En caso negativo visualiza un mensaje de error */ /*----------------------------------------------------*/ #include <iostream> using namespace std; main() { double centig; cout<<"Introduzca grados celsius:"; cin>>centig; if(centig>=-273.) { double kelvin=centig+273.; cout<<centig<<" grados celsius son "<<kelvin <<" grados kelvin\n"; } else { cout<<"Esa temperatura no existe\n"; } }
Sentencias de control de flujo condicional Control de flujo condicional doble // Ejemplo que ilustra el hecho de que hay que tener cuidado
// al realizar comparaciones entre valores en coma flotante #include <iostream> using namespace std; main() { double x=0.5; double y=0.1; double z=0.4; if(x==y+z) // Matemáticamente son iguales, pero ... { cout<<"x es igual a y+z\n"; } else cout<<"x no es igual a y+z\n";
} Salida del programa
Sentencias de control de flujo condicional Control de flujo condicional doble // Ejemplo similar al anterior: no existe asociatividad en
// operaciones en coma flotante
#include <iostream> using namespace std; main() { double x=0.1+(0.2+0.3); double y=(0.1+0.2)+0.3; //Fija 20 posiciones a visualizar despues de la coma cout.precision(20); cout<<"x="<<x<<"\ny="<<y<<endl; if(x==y) // Matemáticamente son iguales, pero ... cout<<"x es igual a y\n"; else cout<<"x no es igual a y\n"; }
Salida del programa
Sentencias de control de flujo condicional
Control de flujo condicional doble if (a != 0)
{
b = a;
a = 1/a;
};
else
cout<<"No se puede invertir el 0\n";
if (a != 0)
b = a;
a = 1/a;
else
cout<<"No se puede invertir el 0\n";
!!Error de sintaxis!! Después del if
está la sentencia vacía por lo que aparece el else desvinculado del if.
!!Error de sintaxis!! El if afecta a la
primera sentencia. La segunda siempre se ejecuta y el else
aparece desvinculado.
Sentencias de control de flujo condicional Control de flujo condicional múltiple: if anidados
Se evalúan varias expresiones condicionales de arriba a abajo. Cuando
aparece una condición verdadera, ejecuta las acciones asociadas y
salta el resto de las expresiones condicionales sin ni siquiera evaluarlas.
Normalmente existe un bloque final else{…} que actúa como condición
por defecto.
if(condicion_1) { bloque 1 de sentencias; } else if(condicion_2) { bloque 2 de sentencias; } ………… else // si no se cumple ninguna de las anteriores { bloque n de sentencias; }
!!Opcional!!
Sentencias de control de flujo condicional
Control de flujo condicional múltiple: if anidados
¿condición1?
Sentencia_1.1
.
.
Sentencia_1.n
Sí
No
Sentencia_2.1
.
.
Sentencia_2.n
¿condición2? No
Sí
Sentencias de control de flujo condicional
Control de flujo condicional múltiple: if anidados #include <iostream> using namespace std; main() { int x=0; int y=1000; cout<<"Introduce un numero entero:"; int z; cin>>z; if(z<x) cout<<"El numero introducido es menor que "<<x<<endl; else if(z<y) cout<<"El numero introducido es menor que "<<y<<endl; else cout<<"El numero introducido es igual o mayor que "<<y<<endl; }
Sentencias de control de flujo condicional Control de flujo condicional múltiple ¿Por qué es mejor la opción 1 si las condiciones son mutuamente excluyentes?
!!Opcional!!
OPCIÓN 1 OPCIÓN 2
if(condicion_1) { bloque 1 de sentencias; } else if(condicion_2) { bloque 2 de sentencias; } ………… else if(condicion_n) { bloque n de sentencias; }
if(condicion_1) { bloque 1 de sentencias; } if(condicion_2) { bloque 2 de sentencias; } ………… if(condicion_n) { bloque n de sentencias; }
Sentencias de control de flujo condicional
Legibilidad en el Control de flujo condicional if(n>0)
if(a>b)
z=a;
else
z=b;
if(n>0)
if(a>b)
z=a;
else
z=b;
if(n>0)
{
if(a>b)
z=a;
}
else
z=b;
El else se asocia al if sin else
más cercano
La legibilidad se mejora sangrando
las líneas
Si deseamos asociar el else al if
externo ésta es la solución
Operadores relacionales lógicos
Sirven para generar expresiones compuestas de tipo
lógico, cuya evaluación puede resultar cierta o falsa.
La evaluación de las operaciones lógicas se realiza
de izquierda a derecha y se interrumpe cuando se
ha asegurado el resultado.
•&& AND Devuelve cierto cuando todas las expresiones
que relaciona son ciertas
•|| OR Devuelve cierto cuando al menos una es cierta
•^ XOR Devuelve cierto sólo cuando una es cierta
•! NOT Devuelve cierto cuando la expresión es falsa
Operadores relacionales lógicos // Ejemplo que controla el rango permitido de valores de dos enteros
#include <iostream> using namespace std; main() { int x,y; cout<<"Introduce dos numeros enteros positivos o 0. "
<<"Ambos no pueden valer 0.\n"; cout<<"Introduce el primer numero:"; cin>>x; cout<<"Introduce el segundo numero:"; cin>>y; if(x<0 || y<0) cout<<"Error. Los numeros introducidos no son ambos "
<<"positivos o uno de ellos cero.\n"; else if(x==0 && y==0) cout<<"Error. Los dos numeros son 0.\n"; else cout<<"Los numeros introducidos son validos.\n"; }
Operadores relacionales lógicos // Ejemplo que controla que un denominador no sea 0
#include <iostream> using namespace std; main() { int x,y; cout<<"Introduce dos numeros enteros.\n"; cout<<"Introduce el primer numero, x:"; cin>>x; cout<<"Introduce el segundo numero, y:"; cin>>y; if(!y) cout<<"Error. No podemos dividir por 0.\n"; else cout<<"x/y="<<x/y<<endl; }
Sentencias de control de flujo condicional
Una estructura condicional es la que realiza un
conjunto u otro de sentencias dependiendo del
cumplimiento o no de una determinada condición.
Podemos distinguir tres tipos:
• Simples: if
• Dobles: if - else
• Múltiples: if – else if - … - else
switch
Sentencias de control de flujo condicional
Aunque el escalonamiento if-else if puede realizar múltiples
comparaciones no es recomendable en ocasiones por la poca
claridad que aporta.
La estructura condicional múltiple llamada switch compara una
variable o expresión entera frente a una lista de constantes enteras (datos int o char). Si se localiza una coincidencia, se
ejecutan las acciones asociadas a la constante. Opcionalmente se puede insertar una condición por defecto mediante default.
switch(expresion) { case valor_1: sentencias_1 case valor_2: sentencias_2 … case valor_n: sentencias_n default: sentencias_default; }
Sentencias de control de flujo condicional Fragmento de código que determina el número de días de un mes: Ejemplo de uso con if anidados (versión 1) if(mes==1)
cout<<"El mes tiene 31 dias\n"; else if(mes==2) cout<<"El mes tiene 28 o 29 dias\n"; else if(mes==3) cout<<"El mes tiene 31 dias\n"; else if(mes==4) cout<<"El mes tiene 30 dias\n"; else if(mes==5) cout<<"El mes tiene 31 dias\n"; else if(mes==6) cout<<"El mes tiene 30 dias\n"; else if(mes==7) cout<<"El mes tiene 31 dias\n"; else if(mes==8) cout<<"El mes tiene 31 dias\n"; else if(mes==9) cout<<"El mes tiene 30 dias\n"; else if(mes==10) cout<<"El mes tiene 31 dias\n"; else if(mes==11) cout<<"El mes tiene 30 dias\n"; else if(mes==12) cout<<"El mes tiene 31 dias\n"; else cout<<"Imposible\n";
Sentencias de control de flujo condicional Fragmento de código que determina el número de días de un mes: Ejemplo de uso con if anidados (versión 2) if(mes==1||mes==3||mes==5||mes==7||mes==8||mes==10||mes==12) cout<<"El mes tiene 31 dias\n"; else if(mes==2) cout<<"El mes tiene 28 o 29 dias\n"; else if(mes==4||mes==6||mes==9||mes==11) cout<<"El mes tiene 30 dias\n"; else cout<<"Imposible\n";
Sentencias de control de flujo condicional Fragmento de código que determina el número de días de un mes: Ejemplo de uso del switch switch(mes) { case 1: case 3: case 5: case 7: case 8: case 10: case 12: cout<<"El mes tiene 31 dias\n"; break; case 4: case 6: case 9: case 11: cout<<"El mes tiene 30 dias\n"; break; case 2: cout<<"El mes tiene 28 o 29 dias\n"; break; default: cout<<"Imposible\n"; }
La sentencia break causa una
salida inmediata del switch.
Sentencias de control de flujo condicional
El switch y los menús
La sentencia switch se utiliza con frecuencia para
gestionar la selección en menús.
En caso de que la opción elegida no sea válida se realiza el código asociado al default, visualizándose
un mensaje de error.
Sentencias de control de flujo condicional
El switch y los menús: ejemplo /*---------------------------------------------------*/ /* Este programa transforma temperaturas de */ /* grados centigrados -> grados kelvin */ /* y de grados kelvin -> grados centigrados */ /* Comprueba si la temperatura tiene existencia real */ /*---------------------------------------------------*/ #include <iostream> using namespace std; main() { int opcion; double centig,kelvin; cout<<"Este programa transforma temperaturas\n"; cout<<"Elija una opcion:\n"; cout<<"1.De grados centigrados a grados kelvin\n"; cout<<"2.De grados kelvin a grados centigrados \n\n"; cout<<"Por favor, introduzca su opcion (1 o 2)\n"; cin>>opcion;
(continúa…)
Sentencias de control de flujo condicional switch(opcion) { case 1: // paso de Celsius a Kelvin cout<<"Introduzca la temperatura en grados Celsius:\n"; cin>>centig; if(centig>=-273) { kelvin=centig+273; cout<<centig<<" grados C son "<<kelvin<<" grados K\n"; } else cout<<"Temperatura por debajo del cero absoluto\n"; break; case 2: // paso de Kelvin a Celsius cout<<"Introduzca la temperatura en grados kelvin:\n"; cin>>kelvin; if(kelvin>=0) { centig=kelvin-273; cout<<kelvin<<" grados K son "<<centig<<" grados C\n"; } else cout<<"Temperatura por debajo del cero absoluto\n"; break; default: cout<<"La opcion escogida no es valida.\n"; }// fin switch }// fin main
Sentencias de control de flujo iterativo
Las estructuras repetitivas o iterativas permiten
repetir una secuencia de instrucciones en tanto no
deje de cumplirse una condición. Estas estructuras
se denominan también bucles.
En el momento que la condición pasa a ser falsa el
control del programa pasa a la línea que sigue al
bucle.
Podemos distinguir tres tipos:
• Bucle while
• Bucle do - while
• Bucle for
Sentencias de control de flujo iterativo
Bucle while
La secuencia de acciones se repetirá mientras la condición sea cierta.
La condición se evalúa al comienzo de la estructura.
Esto supone que el bloque de instrucciones puede no ejecutarse ninguna vez si la condición es inicialmente falsa.
while(condicion)
{
bloque de sentencias;
}
Sentencias de control de flujo iterativo Bucle while: ejemplo /*---------------------------------------------------*/ /* Este programa transforma temperaturas de */ /* grados centigrados -> grados kelvin */ /* Comprueba si la temperatura tiene existencia real */ /* En caso negativo vuelve a pedirla. */ /*---------------------------------------------------*/ #include <iostream> using namespace std; int main() { double centig; cout<<"Introduzca temperatura en grados centigrados:"; cin>>centig; while(centig<-273) { cout<<"Esa temperatura es inferior a -273.\n"; cout<<"Vuelva a introducir la temp. en centig.\n"; cin>>centig; } double kelvin=centig+273; cout<<centig<<" grados C son "<<kelvin<<" grados K\n"; }
Sentencias de control de flujo iterativo
Bucle while: ejemplo
int contador = 20; while(contador) { cout<<"Contador = "<<contador<<endl; contador=contador-1; }
int contador = 0; while(contador) { cout<<"Contador = "<<contador<<endl; contador=contador-1; }
Equivalente a: while(contador!=0)
No entra en el bucle
Sentencias de control de flujo iterativo
Bucle do-while
El bloque de acciones se repetirá hasta que la
condición sea falsa.
Al contrario que en el bucle while, que
comprueba la condición antes de entrar en el bucle, el bucle do-while la evalúa al final del bucle. Esto
implica que el bucle se ejecutará al menos una
vez. do
{
bloque de sentencias;
}while(condicion);
Sentencias de control de flujo iterativo
Bucle do-while
¿condición?
Sentencia_1
.
.
Sentencia_n
Sí
No
Sentencias de control de flujo iterativo Bucle do-while:ejemplo #include <iostream> using namespace std; int main() { int opcion; double centig, kelvin; do { cout<<"Este programa transforma temperaturas\n"; cout<<"Elija una opcion:\n"; cout<<"1.De grados centigrados a grados kelvin\n"; cout<<"2.De grados kelvin a grados centigrados \n\n"; cout<<"Por favor, introduzca su opcion (1 o 2)\n"; cin>>opcion; } while(opcion!=1 && opcion!=2); switch(opcion) { case 1: // paso de centig a Kelvin ..... case 2: // paso de kelvin a centig ..... } // ahora no hace falta poner default }
Operadores decremento e incremento
Actúan sobre un único operando:
• ++ incremento
• -- decremento
Pueden preceder o suceder a su operando:
• Si el operador antecede al operando, C++ utiliza el valor ya incrementado o decrementado. Por ejemplo:
x = 10;
y = ++x;
pone y a 11.
• Sin embargo, si se escribe el código como
x = 10;
y = x++;
y toma el valor de 10. En ambos casos x queda como 11; la diferencia estriba en cuándo cambia el valor.
Cuando se pueda usar indistintamente ++x o x++ es más eficiente ++x dado que con x++ se tiene que guardar el valor hasta la siguiente sentencia.
Operadores decremento e incremento Para añadir 1 al valor de una variable tenemos 3 posibilidades:
1. x=x+1;
2. x += 1;
3. ++x; (o x++;)
Supongamos que la variable x se encuentra almacenada en un registro R de la CPU y sea A otro registro de la CPU donde ésta guarda el resultado de una operación, en este caso la suma +.
1. x=x+1;
1. MOV RA; // Mueve a A el contenido del registro R.
2. ADD A,1; // Suma 1 (podría ser otro valor). Hemos evaluado la expresión a la derecha de =.
3. MOV AR; // Mueve el resultado desde A a la posición R. Es el efecto del operador =.
2. x += 1;
1. ADD R,1; // Suma 1 (podría ser otro valor) directamente al contenido del registro R.
3. ++x;
1. INC (R); // No realiza una suma a nivel hardware. Un circuito especial de la ALU permite
// incrementar o decrementar una variable en una unidad de forma más rápida.
Los compiladores modernos pueden optimizar nuestro código y transformar la x=x+1; a, por ejemplo ++x; de forma transparente al usuario.
Las 3 producen el mismo resultado pero a nivel interno unas son más eficientes que otras.
Sentencias de control de flujo iterativo
Bucle for
La estructura for ejecuta las acciones del bucle un
número específico de veces. Por tanto, cuando el número de iteraciones se conoce de antemano,
lo más normal es el empleo de una estructura de tipo for que
controla automáticamente el número de repeticiones.
Es preciso definir una variable que actúa como
contador, con su valor inicial, su valor final y un
valor fijo de incremento.
for(inicializacion;condicion;incremento)
{
bloque de sentencias;
}
Sentencias de control de flujo iterativo
Bucle for
¿condición?
Sentencia_1
.
.
Sentencia_n
Sí
No
inicialización
incremento
Sentencias de control de flujo iterativo
Bucle for:ejemplo
int i; for(i=1; i<=10; ++i) { cout<<i<<endl; }
¿i<=10?
cout<<i<<endl;
Sí
No
i=1
++i
Sentencias de control de flujo iterativo
Bucle for
Nótese que el incremento puede ser tanto positivo
como negativo.
El incremento no tiene que ser necesariamente de
uno en uno.
Como en el bucle while, en el bucle for se
comprueba la condición al inicio del bucle, lo que
significa que el código del mismo puede no
ejecutarse ni una sola vez.
Sentencias de control de flujo iterativo
Bucle for:ejemplo
int i; for(i=1; i<=10; ++i) { cout<<i<<endl; }
for(i=1; i<=10; i+=2) { cout<<i<<endl; }
Este operador unario sobre el entero i equivale a i=i+1.
Este operador equivale a i=i+2.
Sentencias de control de flujo iterativo Bucle for: ejemplo 1 /*---------------------------------------------------*/ /* Este es un sencillo programa que visualiza */ /* los números enteros entre dos dados. El primer */ /* número consideramos que es mayor que el segundo */ /*---------------------------------------------------*/ #include <iostream> using namespace std; int main() { int comienzo,fin; cout<<"Por que numero empiezo a imprimir?\n"; cin>>comienzo; cout<<"Hasta que numero imprimo?\n"; cin>>fin; for(int i=comienzo;i<=fin; ++i) { cout<<i<<endl; } }
Se ha declarado la variable del contador i en
el propio bucle for. Es una buena práctica
de programación pues nos aseguramos de que es local a la sentencia for.
Sentencias de control de flujo iterativo Bucle for: ejemplo 2 /*---------------------------------------------------*/ /* Este es un sencillo programa que visualiza */ /* los números enteros entre dos dados. El primer */ /* número consideramos que es menor que el segundo */ /*---------------------------------------------------*/ #include <iostream> using namespace std; int main() { int comienzo,fin; cout<<"Por que numero empiezo a imprimir?\n"; cin>>comienzo; cout<<"Hasta que numero imprimo?\n"; cin>>fin; for(int i=comienzo;i>=fin; --i) { cout<<i<<endl; } }
Este operador unario sobre el entero i equivale a i=i-1.
Sentencias de control de flujo iterativo Bucle for: ejemplo 3
// Este programa engloba a los dos anteriores
#include <iostream> using namespace std; int main() { int comienzo,fin; cout<<"Por que numero empiezo a imprimir?\n"; cin>>comienzo; cout<<"Hasta que numero imprimo?\n"; cin>>fin; if(comienzo<=fin) { for(int i=comienzo; i<=fin; ++i) cout<<i<<endl; } else // comienzo > fin { for(int i=comienzo; i>=fin; --i) cout<<i<<endl; } }
Sentencias de control de flujo iterativo Bucle for: ejemplo 4
// Idéntico al ejemplo 3 eliminado llaves en if y else
#include <iostream> using namespace std; int main() { int comienzo,fin; cout<<"Por que numero empiezo a imprimir?\n"; cin>>comienzo; cout<<"Hasta que numero imprimo?\n"; cin>>fin; if(comienzo<=fin) for(int i=comienzo; i<=fin; ++i) cout<<i<<endl; else // comienzo > fin for(int i=comienzo; i>=fin; --i) cout<<i<<endl; }
Cada bloque for constituye
una única sentencia.
Podemos obviar el uso de las llaves.
Sentencias de control de flujo iterativo Bucle for: ejemplo 5 /*--------------------------------------------------------*/ /*---Programa que calcula la media de una cantidad--------*/ /*---indeterminada de números-----------------------------*/ /*--------------------------------------------------------*/ #include <iostream> using namespace std; int main() { int num; cout<<"Introduce numero de valores:"; cin>>num; double valor,acumulador=0; for(int i=0; i<num; ++i) { cout<<"Introduce numero:"; cin>>valor; acumulador+=valor; } double media=acumulador/num; cout<<"La media de los numeros es "<<media<<endl; }
Equivale a acumulador=acumulador+valor;
Sentencias de control de flujo iterativo Bucle for: ejemplo 6 /*--------------------------------------------------------*/ /*---Programa que calcula la media de una cantidad--------*/ /*---indeterminada de números-----------------------------*/ /*--------------------------------------------------------*/ #include <iostream> using namespace std; int main() { int num; cout<<"Introduce numero de valores:"; cin>>num; double acumulador=0; for(int i=0; i<num; ++i) {
double valor; cout<<"Introduce numero:"; cin>>valor; acumulador+=valor; } double media=acumulador/num; cout<<"La media de los numeros es "<<media<<endl; }
Declaramos valor dentro del bucle.
Buena práctica de programación pues es una variable local a la sentencia for.
Sentencias de control de flujo iterativo Bucle for: ejemplo 7
/*-------------------------------------------------------*/ /* Programa que calcula el factorial de un número */ /*-------------------------------------------------------*/ #include <iostream> using namespace std; int main() { int numero; cout<<"Introduce el numero del que quiere hallar su factorial:\n"; cin>>numero; if(numero<0) cout<<"El factorial de un numero negativo no existe\n"; else { int factorial=1; for(int i=numero;i>=1;--i) factorial*=i; cout<<"El factorial de "<<numero<<" es "<<factorial<<endl; } }
Sentencias de control de flujo iterativo
Uso del bucle for, while o do-while
Toda estructura for se puede realizar con una estructura while
de forma relativamente sencilla.
Transformar una estructura while a una estructura for es más
complejo, salvo en aquellos bucles en los que la salida venga
determinada por una variable de tipo contador. Obviamente, en estos casos, lo lógico es usar una estructura tipo for.
•Utilizaremos una sentencia de control for si se sabe antes de
entrar en el bucle el número de veces que tiene que ejecutarse o
existe claramente una variable contador cuyo valor determina la
salida del bucle.
•Si se desconoce de antemano el número de iteraciones optaremos
por una estructura while o do-while. Se elegirá una
estructura de tipo do-while si el código asociado necesariamente
debe ejecutarse al menos una vez.
Sentencias de control de flujo iterativo Uso del bucle for o while: ejemplo // Imprime los pares entre 0 y 100 con un for #include <iostream> using namespace std; int main() { for(int i=0; i<=100; i+=2) cout<<i<<endl; }
// Imprime los pares entre 0 y 100 con un while #include <iostream> using namespace std; int main() { int i=0; while(i<=100) { cout<<i<<endl; i+=2; } }
Saltos incondicionales
Permiten realizar saltos incondicionales dentro de
un bucle o función:
•break: Sale de forma automática del último bucle
abierto (for, while, do while y switch).
•continue: Da un salto hasta el final del último bucle
abierto. (Apenas se usa y no lo estudiaremos)
•goto: Salta a una línea etiquetada del programa. (No
es recomendable usarlo y no lo estudiaremos)
•return: Salida de una función, pudiendo devolver un
valor (Se verá más adelante)
Saltos incondicionales
El salto incondicional break ha sido considerado en muchos ámbitos, sobre todo docentes, 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 es absolutamente recomendable su uso.
Supongamos un problema que, con diferentes variantes, es muy habitual:
Hemos de leer un número máximo de datos (desde teclado o desde un archivo) y realizar con ellos una serie de operaciones. Si se cumple una determinada condición abandonaremos la entrada de datos y daremos el resultado.
Saltos incondicionales // Ejemplo de un buen uso de break
#include <iostream> using namespace std; int main() { int num_datos; cout<<"Numero maximo de datos a leer: "; cin>>num_datos; int i,sumatorio=0; for(i=0; i<num_datos; ++i) { int dato;
cout<<"Introduzca dato (negativo para finalizar): "; cin>>dato; if(dato<0) break; sumatorio+=dato; } cout<<"Los datos validos introducidos son "<<i<<endl; cout<<"La suma de los datos introducidos es "<<sumatorio<<endl; }
No inicializar las variables es un error
habitual. Hacedlo siempre salvo que se
vaya a introducir el valor por teclado o en
las líneas inmediatas, como pasa con las variables num_datos, i y dato.
Saltos incondicionales // Alternativa al ejemplo anterior sin break #include <iostream> using namespace std; int main() { int num_datos; cout<<"Numero maximo de datos a leer: "; cin>>num_datos; int i=0,dato=0,sumatorio=0; while(i<num_datos && dato>=0) { cout<<"Introduzca dato (negativo para finalizar): "; cin>>dato; if(dato>=0) { sumatorio+=dato; ++i; } } cout<<"Los datos validos introducidos son "<<i<<endl; cout<<"La suma de los datos introducidos es "<<sumatorio<<endl; }
¡Cuidado!, hay que inicializar dato
pues lo evaluamos en la condición
Saltos incondicionales // Otra alternativa sin break. Desaconsejada: deberíamos usar for
// sólamente cuando sabemos el número de iteraciones de antemano #include <iostream> using namespace std; int main() { int num_datos; cout<<"Numero maximo de datos a leer: "; cin>>num_datos; int i,dato=0,sumatorio=0; for(i=0; i<num_datos && dato>=0; ++i) { cout<<"Introduzca dato (negativo para finalizar): "; cin>>dato; if(dato>=0) sumatorio+=dato; else --i; // Para compensar el ++i de fin de bloque for } cout<<"Los datos validos introducidos son "<<i<<endl; cout<<"La suma de los datos introducidos es "<<sumatorio<<endl; }
Condición múltiple, pérdida de legibilidad
Ejemplos Simulación de la Multiplicación Hardware
Descripción
El producto de dos enteros positivos 𝑥 e 𝑦, 𝑃 = 𝑥 ∗ 𝑦 podemos calcularlo de forma
iterada introduciendo un término 𝑡𝑖 en la expresión, de tal forma que, en la iteración 0:
𝑃 = 𝑡0 + 𝑥0 ∗ 𝑦0 𝑐𝑜𝑛 𝑡0 = 0, 𝑥0 = 𝑥, 𝑦0 = 𝑦
La iteración 𝑖 + 1 se obtiene de la anterior 𝑖 de la siguiente forma:
• Si 𝑦𝑖 es un número par (en binario su digito más a la derecha es un 0)
• 𝑦𝑖+1 = 𝑦𝑖/2 Desplazamiento de un bit en hardware hacia la derecha
• 𝑥𝑖+1 = 𝑥𝑖∗ 2 Desplazamiento de un bit en hardware hacia la izquierda
• 𝑡𝑖+1 = 𝑡𝑖
El valor de 𝑃 no varía: 𝑃 = 𝑡𝑖 + 𝑥𝑖 ∗ 𝑦𝑖 = 𝑡𝑖 + (𝑥𝑖∗ 2) ∗ (𝑦𝑖/2) = 𝑡𝑖+1 + 𝑥𝑖+1 ∗ 𝑦𝑖+1
• Si 𝑦𝑖 es un número impar (en binario su digito más a la derecha es un 1)
• 𝑦𝑖+1 = 𝑦𝑖 − 1 Cambiar el bit más a la derecha a 0
• 𝑥𝑖 = 𝑥𝑖
• 𝑡𝑖+1 = 𝑡𝑖 + 𝑥𝑖
El valor de 𝑃 no varía: 𝑃 = 𝑡𝑖 + 𝑥𝑖 ∗ 𝑦𝑖 = 𝑡𝑖 + 𝑥𝑖 + 𝑥𝑖 ∗ (𝑦𝑖 − 1) = 𝑡𝑖+1 + 𝑥𝑖+1 ∗ 𝑦𝑖+1
El proceso finalizará en la iteración 𝑛 en la que 𝑦𝑛 = 0, y entonces 𝑃 = 𝑡𝑛.
Ejemplos Simulación de la Multiplicación Hardware
Descripción
Ejemplo:
Iteración 0 1 2 3 4 5 6
x 27 54 54 108 216 423 432
y 18 9 8 4 2 1 0
t 0 0 54 54 54 54 486
Ejemplos Simulación de la Multiplicación Hardware
Diseño del programa
Datos de entrada
Se solicitará al usuario que introduzca dos valores enteros x e y.
Datos de salida
Se mostrará por pantalla el producto x*y con un mensaje del tipo:
(x)*(y)=p
Ejemplos Simulación de la Multiplicación Hardware Diseño del programa
Procesamiento de los datos
Si alguno de los datos de entrada es 0, el programa terminará con producto=0;
El usuario introducirá valores enteros, positivos y negativos. Dado que el algoritmo
está diseñado para números enteros positivos deberemos tener en cuenta el signo con una variable signo tal que:
• Si el signo de x e y coincide, signo=1;
• Si el signo de x e y difiere, signo=-1;
Puesto que el término 𝑡𝑖 acaba convergiendo al valor del producto, no hace falta declarar una variable específica, por lo que usaremos producto para representarlo.
Para realizar las iteraciones usaremos un bucle while, puesto que no sabemos
cuantas iteraciones serán necesarias. La salida del bucle se producirá cuando la expresión (y) sea falsa.
¿Cómo programamos expresiones tales como 𝑥𝑖+1 = 𝑥𝑖∗ 2?
Simplemente con x=x*2;. El valor de x (𝑥𝑖) a la derecha de = se pierde (iteración 𝑖) para obtener un nuevo valor x (𝑥𝑖+1) a la izquierda de = (iteración 𝑖 + 1).
Dentro del while solo queda establecer los nuevos valores de x, y y producto en
función de si y es par o impar.
Ejemplos Simulación de la Multiplicación Hardware #include <iostream> using namespace std; int main() { int x,y; cout<<"Introduzca valor x:"; cin>>x; cout<<"Introduzca valor y:"; cin>>y; int producto=0; int signo=1; cout<<"("<<x<<")*("<<y<<")="; if(x!=0 && y!=0) // Si alguno es 0 no hacemos nada, producto=0. { if((x>0 && y<0) || (x<0 && y>0)) { signo=-1; } if(x<0) x*=-1; if(y<0) y*=-1;
Continúa…
Este cout lo ponemos aquí puesto que x e y
varían su valor a lo largo de las iteraciones
Una vez calculado signo,
hacemos que x e y sean positivas
Ejemplos Simulación de la Multiplicación Hardware
while(y) { if(y%2==0) { x=x*2; y=y/2; } else { producto+=x; y=y-1; } } } cout<<signo*producto<<endl; }
Sabemos que y>0 al menos una vez.
Ejemplos Sucesiones homogéneas recurrentes de orden 2
Descripción Una sucesión recurrente homogénea de orden 𝑡 es aquella cuyos términos
vienen dados por la expresión:
𝑥𝑛+𝑡 = 𝑐1𝑥𝑛+𝑡−1 + 𝑐2𝑥𝑛+𝑡−2 + ⋯ + 𝑐𝑡−1𝑥𝑛+1 + 𝑐𝑡𝑥𝑛
Nótese que para poder generar la sucesión necesitamos los 𝑡 primeros términos.
Dado que aún no hemos estudiado vectores, vamos a centrarnos en las
sucesiones homogéneas recurrentes de orden 2:
𝑥𝑛+2 = 𝑐1𝑥𝑛+1 + 𝑐2𝑥𝑛
Un ejemplo de sucesión recurrente homogénea de orden 2 es la sucesión de
Fibonacci, que viene definida por:
• coeficientes 𝑐1 = 𝑐2 = 1
• valores iniciales 𝑥0 = 0 y 𝑥1 = 1
La sucesión fue dada a conocer en occidente por Leonardo de Pisa (alias
Fibonacci) como la solución a un problema de la cría de conejos: A partir de una pareja de conejos en una granja, cuantas parejas se obtendrían al cabo
de un año sabiendo que:
• Una pareja desde que nace tarda dos meses en reproducirse
• A partir del 2º mes, se reproduce cada mes
• Cada camada es otra pareja
Ejemplos Sucesiones homogéneas recurrentes de orden 2
Descripción La evolución de las parejas de conejos es la siguiente:
1. Al inicio del primer periodo se introduce una pareja.
2. Al inicio del segundo periodo solamente tenemos una pareja.
3. Al inicio del tercer periodo (han pasado dos meses) tenemos la pareja inicial mas
sus crías, en total, 2 parejas.
4. Al inicio del cuarto periodo se ha vuelto a reproducir la pareja inicial, por lo que
tenemos 3 parejas, y así sucesivamente.
La sucesión de Fibonacci vendría dada entonces por 0,1,1,2,3,5,8,13,21,34,…
Período Parejas Total
0 0
1 1 1
2 1 1
3 1 1 2
4 1 1 1 3
5 1 1 1 1 1 5
Ejemplos Sucesiones homogéneas recurrentes de orden 2 El programa deberá calcular los N primeros términos de la sucesión, su suma y el
ratio entre el último elemento calculado y el anterior de la sucesión.
En el caso de la sucesión de Fibonacci, el ratio 𝑥𝑛/𝑥𝑛−1 tiende asintóticamente al
número áureo 𝝋,de gran importancia en diseño y arquitectura y presente en muchos
fenómenos naturales: Dados 2 segmentos de longitudes 𝑎 y 𝑏, con 𝑎 > 𝑏:
𝜑 =𝑎
𝑏=
𝑎+𝑏
𝑎=
1+ 5
2= 1.618033988749…
Diseño del programa
Sin pérdida de generalidad, supondremos coeficientes y valores iniciales enteros.
Datos de entrada
Se solicitará al usuario que introduzca:
• los coeficientes c1 y c2.
• los 2 primeros términos de la sucesión t1 y t2.
• El número de términos num_terminos a calcular. Se exigirá que sea mayor que 2.
Datos de salida
Según vayan obteniéndose, se mostrarán por pantalla cada uno de los num_terminos de la sucesión. Al finalizar el programa, se mostrará la suma de los N
términos y el ratio solicitado.
Ejemplos Sucesiones homogéneas recurrentes de orden 2 Diseño del programa: Procesamiento de los datos
El programa exigirá que el número de términos sea mayor que 2. Para ello, se utilizará un bucle do while.
Inicializaremos el sumatorio, suma, de los términos a suma=t1+t2;
Dado que sabemos cuantos términos hay que calcular, el bucle de iteración adecuado es for. Como disponemos de los 2 primeros valores, el bucle se inicializará con el
contador i igual a 2 y permanecerá mientras i<num_terminos.
¿Cómo programamos expresiones recurrentes tales como 𝑥𝑛+2 = 𝑐1𝑥𝑛+1 + 𝑐2𝑥𝑛?
Necesitamos usar 3 variables: t1 𝑥𝑛 , t2 𝑥𝑛+1 y nuevo_termino 𝑥𝑛+2
Si la sucesión hubiese sido de orden 3 necesitaríamos 4 variables, etc. Para un orden
general 𝑛 el programa debería usar vectores, que veremos más adelante.
Una vez obtenido nuevo_termino=c1*t2+c2*t1; basta actualizar para la
siguiente iteración los nuevos valores de t1 y t2:
t1=t2;
t2=nuevo_termino;
También actualizaremos el valor del sumatorio de términos.
Fuera del bucle calcularemos el ratio, ratio, como ratio=t2/t1;
Ejemplos Sucesiones homogéneas recurrentes de orden 2 #include <iostream> using namespace std; int main() { int num_terminos; do { cout<<"Introduzca el numero de terminos:"; cin>>num_terminos; } while(num_terminos<=2); int c1,c2; cout<<"Introduzca coeficiente c1 (Xn2=c1*Xn1+c2*Xn):"; cin>>c1; cout<<"Introduzca coeficiente c2 (Xn2=c1*Xn1+c2*Xn):"; cin>>c2; int t1,t2; cout<<“Termino de la sucesion x0 (Xn2=c1*Xn1+c2*Xn):"; cin>>t1; cout<<“Termino de la sucesion x1 (Xn2=c1*Xn1+c2*Xn):"; cin>>t2;
Continúa…
Ejemplos Sucesiones homogéneas recurrentes de orden 2 int suma=t1+t2; cout<<"Los terminos de la sucesion son "<<t1<<" "<<t2; for(int i=2;i<num_terminos;++i) { int nuevo_termino=c1*t2+c2*t1; cout<<" "<<nuevo_termino; t1=t2; t2=nuevo_termino; suma+=nuevo_termino; } cout.precision(20); double ratio=static_cast<double>(t2)/t1; cout<<"\nLa suma de los terminos es "<<suma<<endl; cout<<"\nEl ratio es "<<ratio<<endl; }
Fijamos una precisión de 20 cifras
decimales para los números reales
División entre enteros: conversión explícita para promocionar uno de los int a double.
Ejemplos Control de aforo en un recinto Descripción En una sala de un importante museo se encuentran expuestas valiosas obras de arte.
Para preservarlas y permitir que los visitantes puedan disfrutar sin aglomeraciones, la
dirección del museo ha fijado un aforo máximo de 5 personas.
Para llevar a cabo este control se ha contratado a un vigilante y se han instalado dos
tornos: uno de sólo entrada y otro de sólo salida.
Ambos tornos tienen un sensor que activa un contador que almacena las veces que
una persona ha entrado o salido respectivamente.
Además, están conectados entre sí, de tal forma que si se completa el aforo máximo
el torno de entrada se bloquea. Si durante una jornada el torno de salida se activa
más veces que el de entrada, se lanza un aviso al vigilante: ¡alguien entró sin activar
el torno de entrada o el sistema de control arrancó con personas dentro del recinto!
El vigilante tiene la potestad de:
• resetear con un pulsador los contadores de los tornos (es su responsabilidad
asegurarse de que no hay nadie en la sala). El sistema le pedirá confirmación.
• dar por finalizada la jornada, activando un pulsador que desconecta eléctricamente
los tornos. Lógicamente, si hay personas dentro de la sala el sistema no obedecerá
y lanzará un aviso.
El vigilante tendrá a su disposición un monitor donde visualizará en todo momento el
número de personas dentro de la sala y, al final de la jornada, el número total de
visitantes.
Ejemplos Control de aforo en un recinto Diseño del programa Datos de entrada El programa debe simular las señales de activación de los tornos y del vigilante. Una posible forma es usar una variable char a modo de código, codigo:
'+' Torno de entrada activado
'-' Torno de salida activado
'r' El vigilante resetea la instalación
'f' El vigilante da por finalizada la jornada
Otro dato de entrada, que vamos a utilizar como const en este ejemplo es el aforo
máximo, aforo.
Datos de salida Cada vez que se active una señal, el programa mostrará el número de personas, num_personas, presente en la sala.
Además, al finalizar la jornada, se mostrara el número total de visitantes durante la jornada, num_visitantes.
Se lanzarán mensajes por pantalla si: • La entrada '+' se activa con num_personas==aforo
• La entrada '-' se activa con num_personas==0
• Se activa 'r', para solicitar confirmación
• La entrada 'f' se activa con num_personas>0
Ejemplos Control de aforo en un recinto Diseño del programa Procesamiento de los datos Inicialmente num_personas=num_visitantes=0 y aforo=5.
El programa deberá simular una sesión de trabajo del vigilante/tornos durante la que
se producirá una secuencia de longitud indeterminada de señales, que hemos codificado como '+', '-', 'r' y 'f'.
Por ejemplo: +++--+--rf
Estas señales las simularemos introduciendo los caracteres por teclado y la secuencia usando un bucle do while.
• No usamos while por que al menos una vez introduciremos un código
• No usamos for porque desconocemos cuantos eventos ( entradas y salidas de
visitantes y activaciones de pulsadores por parte del vigilante) tendremos al
cabo de la sesión.
El bucle finalizará cuando el código sea 'f'. Un bucle diseñado para finalizar cuando
un elemento de una secuencia toma un determinado valor se denomina bucle
controlado por centinela.
Tras salir del bucle, se muestra el número de visitantes durante la sesión, num_visitantes.
Ejemplos Control de aforo en un recinto Diseño del programa Procesamiento de los datos La gestión del código, codigo, la haremos con un switch.
• Caso '+'
Si no está completado el aforo, incrementamos num_personas y
num_visitantes.
En caso contrario, lanzamos mensaje. • Caso '-'
Si el aforo no está vacío, decrementamos num_personas.
En caso contrario, lanzamos mensaje. • Caso 'r'
Solicitamos confirmación. • Caso 'f'
Si el aforo no está vacío, lanzamos un aviso y anulamos codigo para que no
salgamos del bucle.
Ejemplos Control de aforo en un recinto #include <iostream> using namespace std; int main() { const int aforo=5; int num_personas=0; int num_visitantes=0; char codigo; do { cout<<"Introduzca codigo" <<"(+ para entrada, - para salida“
<< ", r para reset, f para fin):"; cin>>codigo; switch(codigo) {
Continúa…
Ejemplos Control de aforo en un recinto
case '+': if(num_personas<aforo) { ++num_personas; ++num_visitantes; } else cout<<"AVISO: torno de entrada bloqueado\n"; break; case '-': if(num_personas>0) --num_personas; else cout<<"AVISO: alguien se colo\n"; break;
Continúa…
Ejemplos Control de aforo en un recinto
case 'r': case 'R': cout<<"Confirme por favor:"; cin>>codigo; if(codigo=='r' || codigo=='R') num_personas=0; break; case 'f': case 'F': if(num_personas>0) { cout<<"AVISO: personas en el recinto\n"; codigo='0'; } break; default: break; } // Fin del switch
Continúa…
Por si están activadas las mayúsculas
Borramos el codigo f.
Ejemplos Control de aforo en un recinto
cout<<"Personas en recinto: "<<num_personas<<endl; } while(codigo!='f' && codigo!='F'); cout<<"Num visitantes total: "<<num_visitantes<<endl; }
Ejemplos Juego Tres en raya
Descripción
Tres en raya es un juego entre dos jugadores, O y X, que marcan las
casillas de un tablero 3×3 alternadamente.
Un jugador gana si consigue tener una línea con sus tres símbolos: la
línea puede ser horizontal, vertical o diagonal.
Es un juego en el que si las decisiones son correctas siempre termina en
empate.
Ejemplos Juego Tres en raya Diseño del programa
Datos de entrada
El tablero es fácil de modelar como una matriz 3x3, por lo que los jugadores deberán introducir por teclado la fila, fil, y columna, col, de una casilla. Los
valores permitidos para ambos parámetros serán 0, 1 y 2.
Otro dato de entrada es quien inicia el turno del juego. Podemos, sin pérdida de
generalidad, asociar turno==1 al jugador que usa X’s y turno==2 al jugador
que usa O’s. Los valores 1 y 2 para turno son muy útiles ya que de forma
sencilla, en un cambio de turno, se obtiene uno del otro con turno=turno%2+1;
Un valor inicial de configuración es el máximo numero de jugadas posibles, 9, que podemos declarar como const int num_max_jugadas=9;.
Datos de salida
Después de que un jugador elija las coordenadas de una casilla, se mostrará un
mensaje de error si las coordenadas no son válidas o si la casilla ya está ocupada.
Una vez validadas las coordenadas, se mostrará por pantalla el nuevo tablero usando caracteres 'X' y 'O'.
Una vez finalizado el juego, se mostrará el resultado: ganan X’s, ganan O’s o tablas.
Ejemplos Juego Tres en raya Diseño del programa
Procesamiento de los datos
A estas alturas del curso no hemos visto funciones ni matrices, por lo que el
código que vamos a obtener va a ser mucho más largo y tedioso de programar.
Sin embargo, en próximos temas lo mejoraremos sustancialmente.
Para codificar el estado de las 9 casillas utilizaremos 9 variables int que
llamaremos c00, c01, …, c22 e inicializaremos a 0.
El identificador cxy sería un acrónimo de casilla en fila x y columna y.
Cuando un jugador, identificado por turno, elije una casilla se hace la asignación
cxy=turno;
La elección del turno de inicio la haremos solicitando al usuario que elija 'x' o
'X' para el jugador X o cualquier otro carácter para el jugador O.
Ejemplos Juego Tres en raya
Diseño del programa: Procesamiento de los datos Bucle de control del estado del juego
El juego consiste en iteradamente solicitar a un jugador que elija unas coordenadas. Podríamos optar por usar un bucle for puesto que sabemos que
como mucho habrá 9 jugadas, pero preferimos usar un bucle do while (while
sería factible también) dado que no siempre se completarán las 9 jugadas.
Para controlar el fin del juego y, por tanto, del bucle do while, elegimos 2
variables int:
• ganador, inicializado a 0 y que toma el valor turno si se detecta un ganador.
• num_jugadas, inicializado a 0 y que se incrementa en cada cambio de turno.
Así, el bucle finalizará cuando sea falsa la condición:
(!ganador && num_jugada<num_max_jugadas)
El cuerpo del bucle realizará secuencialmente las siguientes operaciones:
1. Solicitud, validación de coordenadas y asignación de casilla
2. Incrementar el número de jugadas y cambiar de turno
3. Comprobación de si hay un ganador
4. Mostrar tablero por pantalla
El orden de las operaciones 2, 3 y 4 es indiferente.
Ejemplos Juego Tres en raya
Diseño del programa: Procesamiento de los datos Bucle de control del estado de una jugada
Cuando un jugador introduce las coordenadas fil y col debemos garantizar que
son válidas. Como no sabemos de antemano cuantos intentos realizará el usuario
antes de introducir coordenadas válidas y, al menos tendrá que introducirlas una vez, el bucle apropiado vuelve a ser do while.
Para controlar el fin de proceso de introducción de una jugada y, por tanto, del bucle do while, elegimos una variable int error, inicializada a 0 y que:
• toma el valor 1 si el usuario introduce coordenadas fuera de rango.
• toma el valor 2 si las coordenadas corresponden a una casilla ya ocupada.,
• no cambia de valor si las coordenadas son OK.
Así, el bucle finalizará cuando sea falsa la condición (error)
Las variables error de este bucle y ganador del bucle de control del estado del
juego suelen denominarse variables bandera: su valor determina la ejecución o no de
otras partes del programa.
El cuerpo del bucle realizará secuencialmente las siguientes operaciones: 1. Solicitar coordenadas y establecer valor de error o asignar casilla.
2. Si fuera de rango, error 1, mostrar mensaje 3. Si casilla ocupada, error 2, mostrar mensaje
Ejemplos Juego Tres en raya Pseudocódigo de los bucles principales const int num_max_jugadas=9; int num_jugada=0;
int ganador=0; // Bandera del bucle.
do // BUCLE QUE CONTROLA ESTADO DEL JUEGO { int error; // Bandera del bucle. do // BUCLE QUE CONTROLA ESTADO DE UNA JUGADA
{ error=0; solicita_coordenadas fil y col
if fil y col están fuera de rango error 1
else if fil y col es una casilla ocupada error 2 else es casilla libre rellenamos con valor turno
if(error==1) cout<<"Coordenadas fuera de rango\n"; else if(error==2) cout<<"Casilla ocupada\n";
} while(error);
++num_jugada; turno=turno%2+1; //Cambio de turno Si hay tres en raya cambio en valor ganador: 1 o 2 Se dibuja el tablero
}while(!ganador && num_jugada<num_max_jugadas);
Ejemplos Juego Tres en raya #include <iostream> using namespace std; int main() { int c00=0,c01=0,c02=0,c10=0,c11=0,c12=0,c20=0,c21=0,c22=0; char opcion; cout<<"Pulse X si desea comenzar con cruces.\n" <<"Cualquier otra tecla para comenzar por O.\n" <<"Opcion:"; cin>>opcion; int turno; if(opcion=='x' || opcion=='X') turno=1; else turno=2;
Continúa…
Ejemplos Juego Tres en raya
const int num_max_jugadas=9; int num_jugada=0;
int ganador=0; // Bandera del bucle.
do // Bucle que finaliza cuando se existe un ganador.
{ int error; do // Bucle que finaliza cuando datos fil y col son OK. { error=0; // Se solicitan coordenadas cout<<"Introduzca fila(0,1,2) y columna(0,1,2):"; int fil,col; cin>>fil>>col; if(fil<0 || fil>2 || col<0 || col>2) error=1; else { // Coordenadas validas
Continúa…
Ejemplos Juego Tres en raya if(fil==0) // Coordenada fil 0 { if(col==0) { if(c00==0) c00=turno; else error=2; } else if(col==1) { if(c01==0) c01=turno; else error=2; } else { if(c02==0) c02=turno; else error=2; } } Continúa…
Ejemplos Juego Tres en raya else if(fil==1) // Coordenada fil 1 { if(col==0) { if(c10==0) c10=turno; else error=2; } else if(col==1) { if(c11==0) c11=turno; else error=2; } else { if(c12==0) c12=turno; else error=2; } } Continúa…
Ejemplos Juego Tres en raya else // Coordenada fil 2 { if(col==0) { if(c20==0) c20=turno; else error=2; } else if(col==1) { if(c21==0) c21=turno; else error=2; } else { if(c22==0) c22=turno; else error=2; } }
Continúa…
Ejemplos Juego Tres en raya
} // Fin else coordenadas validas if(error==1) cout<<"Coordenadas fuera de rango\n"; else if(error==2) cout<<"Casilla ocupada\n";
} while(error); ++num_jugada; turno=turno%2+1; //Cambio de turno
Continúa…
Ejemplos Juego Tres en raya if(c11) //Verificamos 4 rayas que pasan por c11 { if(c10==c11 && c11==c12) //Fila 1 ganador=c11; else if(c01==c11 && c11==c21) //Columna 1 ganador=c11; else if(c00==c11 && c11==c22) //Diagonal principal ganador=c11; else if(c02==c11 && c11==c20) //Diagonal secundaria ganador=c11; } if(!ganador && c00) //Verificamos 2 rayas que pasan por c00 { if(c00==c01 && c01==c02) //Fila 0 ganador=c00; else if(c00==c10 && c10==c20) //Columna 0 ganador=c00; } if(!ganador && c22) //Verificamos 2 rayas que pasan por c22 { if(c20==c21 && c21==c22) //Fila 2 ganador=c22; else if(c02==c12 && c12==c22) //Columna 2 ganador=c22; }
Continúa…
Ejemplos Juego Tres en raya // Se dibuja el tablero
// Fila 0 del tablero if(c00==1) cout<<"X "; else if(c00==2) cout<<"O "; else cout<<" "; if(c01==1) cout<<"X "; else if(c01==2) cout<<"O "; else cout<<" "; if(c02==1) cout<<"X "; else if(c02==2) cout<<"O "; cout<<endl;
Continúa…
Ejemplos Juego Tres en raya // Fila 1 del tablero if(c10==1) cout<<"X "; else if(c10==2) cout<<"O "; else cout<<" "; if(c11==1) cout<<"X "; else if(c11==2) cout<<"O "; else cout<<" "; if(c12==1) cout<<"X "; else if(c12==2) cout<<"O "; cout<<endl;
Continúa…
Ejemplos Juego Tres en raya // Fila 2 del tablero if(c20==1) cout<<"X "; else if(c20==2) cout<<"O "; else cout<<" "; if(c21==1) cout<<"X "; else if(c21==2) cout<<"O "; else cout<<" "; if(c22==1) cout<<"X "; else if(c22==2) cout<<"O "; cout<<endl; } while(!ganador && num_jugada<num_max_jugadas);
Continúa…
Ejemplos Juego Tres en raya
if(ganador==1) cout<<"\nGanan X's\n"; else if(ganador==2) cout<<"\nGanan O's\n"; else cout<<"\nTablas\n"; }
Bibliografía
115
• S. B. Lippman, J. Lajoie, B. E. Moo. C++ Primer. Addison-Wesley. 2012.
• V. Benjumea y M.Roldán. Dpto. Lenguajes y Ciencias de la Computación. Fundamentos de Programación con el Lenguaje de Programación C++. Universidad de Málaga.
• http://programmers.stackexchange.com/questions/134118/why-are-shortcuts-like-x-y-considered-good-practice