operadores, expresiones y sentenciasfinf/t3.pdf · Índice 1. Ámbito de las variables 2....

115
Fundamentos de Informática Departamento de Ingeniería de Sistemas y Automática. EII. Universidad de Valladolid T3 OPERADORES, EXPRESIONES Y SENTENCIAS

Upload: doannguyet

Post on 01-Mar-2019

222 views

Category:

Documents


0 download

TRANSCRIPT

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

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

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

No

Sentencia_2.1

.

.

Sentencia_2.n

¿condición2? No

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

¿condición?

Sentencia_1

.

.

Sentencia_n

No

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

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

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;

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