Академический Документы
Профессиональный Документы
Культура Документы
Curso 2012/2013
El color marrn se utilizar para los ttulos de las secciones, apartados, etc El color azul se usar para los trminos cuya denicin aparece por primera vez. En primer lugar aparecer el trmino en espaol y entre parntesis la traduccin al ingls. El color rojo se usar para destacar partes especialmente importantes
denota contenido que el alumno debe estudiar por su cuenta. Entra como materia en el examen. denota contenido de ampliacin. El texto incluido en la transparencia es de obligada lectura aunque las referencias son de consulta opcional y no entran en el examen. denota contenido de ampliacin, a nivel de experto. No entra en el examen.
denota cdigo o prcticas de programacin que pueden producir errores lgicos graves
denota cdigo bien diseado que nos ha de servir de modelo en otras construcciones.
Resea histrica.
Principio de Programacin.
Contenidos
I. Introduccin a la Programacin I.1. El ordenador, algoritmos y programas . . . . . . . . . . . . I.1.1. El Ordenador: Conceptos Bsicos . . . . . . . . . . I.1.2. Lenguajes de programacin . . . . . . . . . . . . . . I.1.3. Datos y Algoritmos . . . . . . . . . . . . . . . . . . . I.1.4. Compilacin . . . . . . . . . . . . . . . . . . . . . . I.2. Especicacin de programas . . . . . . . . . . . . . . . . . I.2.1. Escritura y organizacin de un programa . . . . . . . I.2.2. Elementos bsicos de un lenguaje de programacin I.2.3. Tipos de errores en la programacin . . . . . . . . . I.3. Datos y tipos de datos . . . . . . . . . . . . . . . . . . . . . I.3.1. Representacin en memoria de datos e instrucciones I.3.2. Datos y tipos de datos . . . . . . . . . . . . . . . . . I.4. Operadores y expresiones . . . . . . . . . . . . . . . . . . 1 2 2 3 6 11 13 13 19 22 24 24 26 38 38
I.4.2. Operadores en Programacin . . . . . . . . . . . . . I.4.3. Expresiones . . . . . . . . . . . . . . . . . . . . . . I.5. Tipos de datos comunes en C++ . . . . . . . . . . . . . . . I.5.1. Rango y operadores aplicables a un tipo de dato . . I.5.2. Los tipos de datos enteros . . . . . . . . . . . . . .
39 41 43 43 44 54 66 69 78 81 87 90
I.5.3. Los tipos de datos reales . . . . . . . . . . . . . . . I.5.4. Operando con tipos numricos distintos . . . . . . . I.5.5. El tipo de dato carcter . . . . . . . . . . . . . . . . I.5.6. El tipo de dato cadena de caracteres . . . . . . . . . I.5.7. El tipo de dato lgico o booleano . . . . . . . . . . . I.5.8. El tipo enumerado . . . . . . . . . . . . . . . . . . . II. Estructuras de Control II.1. Estructura condicional . . . . . . . . . . . . . . . . . . . .
91 92
II.1.2. Condicional doble . . . . . . . . . . . . . . . . . . . 103 II.1.3. Anidamiento de estructuras condicionales . . . . . . 109 II.1.4. Construccin de estructuras condicionales . . . . . 122 II.1.5. Estructura condicional mltiple . . . . . . . . . . . . 137 II.2. Estructuras repetitivas . . . . . . . . . . . . . . . . . . . . 143
II.2.1. Bucles controlados por condicin: pre-test y post-test 143 II.2.2. Bucles controlador por contador . . . . . . . . . . . 169
Tema I
Introduccin a la Programacin
Objetivos: Introducir los conceptos bsicos de programacin, para poder construir los primeros programas. Introducir los principales tipos de datos disponibles en C++ para representar informacin del mundo real. Enfatizar, desde un principio, la necesidad de seguir buenos hbitos de programacin.
Autor: Juan Carlos Cubero. Sugerencias: por favor, enviar un e-mail a JC.Cubero@decsai.ugr.es
I.1.
I.1.1.
"Los ordenadores son intiles. Slo pueden darte respuestas". Pablo Picasso
Hardware Software Usuario (User) Programador (Programmer) Lenguaje de Programacin (Programming Language)
I.1.2.
Lenguajes de programacin
Programa (Program) : Es el conjunto de instrucciones especicadas en un lenguaje de programacin concreto, que pueden ejecutarse en un ordenador y que resuelven un problema. Lenguaje de programacin (Programming language) : Lenguaje formal utilizado para comunicarnos con un ordenador e imponerle la ejecucin de un conjunto de rdenes. Cdigo binario (Binary code) :
"There are 10 types of people in the world, those who can read binary, and those who cant".
Lenguaje ensamblador (Assembly language) . Depende del micropocesador (Intel 8086, Motorola 88000, etc) Se usa para programar drivers, microcontroladores (que son circuitos integrados que agrupan microprocesador, memoria y perifricos), compiladores, etc. Se ve en otras asignaturas.
.model small .stack .data Cadena1 DB 'Hola Mundo.$' .code mov ax, @data mov ds, ax mov dx, offset Cadena1 mov ah, 9 int 21h end
Lenguajes de alto nivel (High level language) (C, C++, Java, Lisp, Prolog, Perl, Visual Basic, C#, Go ...) En esta asignatura usaremos C++ (ISO C++).
#include <iostream> using namespace std; int main(){ cout << "Hola Mundo"; }
Resea histrica del lenguaje C++ 1967 Martin Richards: BCPL para escribir S.O. 1970 Ken Thompson: B para escribir UNIX (inicial) 1972 Dennis Ritchie: C 1983 Comit Tcnico X3J11: ANSI C 1983 Bjarne Stroustrup: C++ 1989 Comit tcnico X3J16: ANSI C++ 1990 Internacional Standarization Organization http://www.iso.org Comit tcnico JTC1: Information Technology Subcomit SC-22: Programming languages, their environments and system software interfaces. Working Group 21: C++
http://www.open-std.org/jtc1/sc22/wg21/
2011 Nuevo estndar. Buscar C++11 en Internet. Qu programas se han hecho en C++? Google, Amazon, sistema de reservas areas (Amadeus), omnipresente en la industria automovilstica y area, sistemas de telecomunicaciones, el explorador Mars Rovers, el proyecto de secuenciacin del genoma humano, videojuegos como Doom, Warcraft, Age of Empires, Halo, la mayor parte del software de Microsoft y una gran parte del de Apple, la mquina virtual Java, Photoshop, Thunderbird y Firefox, MySQL, OpenOfce, etc. Entrevista reciente a Bjarne Stroustrup:
http://www.wired.com/thisdayintech/2010/10/1014cplusplus-released/ all/1#ixzz12guDpzz8
I.1.3.
Datos y Algoritmos
Algoritmo (Algorithm) : es una secuencia ordenada de instrucciones que resuelve un problema concreto, atendiendo a las siguientes caractersticas bsicas: Correccin (sin errores). Precisin (no puede haber ambigedad). Repetitividad (en las mismas condiciones, al ejecutarlo, siempre se obtiene el mismo resultado). Finitud (termina en algn momento). Nmero nito de rdenes no implica nitud. Caractersticas esenciales: Validez (resuelve el problema pedido) Eciencia (lo hace en un tiempo aceptable)
Un dato (data) es una representacin simblica de una caracterstica o propiedad de una entidad. Los algoritmos operan sobre los datos. Usualmente, reciben unos datos de entrada con los que operan, y a veces, calculan unos nuevos datos de salida.
Ejemplo. Algoritmo de la media aritmtica de N valores. Datos de entrada: N, valor1, valor2, ..., valorN Datos de salida: media Instrucciones en lenguaje natural: Sumar los N valores y dividir el resultado por N Ejemplo. Algoritmo para la resolucin de una ecuacin de primer grado ax + b = 0 Datos de entrada: a, b Datos de salida: x Instrucciones en lenguaje natural: Calcular x como el resultado de la divisin b/a
Podra mejorarse el algoritmo contemplando el caso de ecuaciones degeneradas
Ejemplo. Algoritmo para el clculo de la hipotenusa de un tringulo rectngulo. Datos de entrada: lado1, lado2 Datos de salida: hipotenusa Instrucciones en lenguaje natural: hipotenusa = lado12 + lado22
Ejemplo. Algoritmo para ordenar una lista de valores numricos. (9, 8, 1, 6, 10, 4) (1, 4, 6, 8, 9, 10) Datos de entrada: el vector Datos de salida: el mismo vector Instrucciones en lenguaje natural: Calcular el mnimo valor de todo el vector Intercambiarlo con la primera posicin Volver a hacer lo mismo con el vector formado por todas las componentes menos la primera. (9, 8, 1, 6, 10, 4) (1, 8, 9, 6, 10, 4) (X, 8, 9, 6, 10, 4) (X, 4, 9, 6, 10, 8) (X, X, 9, 6, 10, 8)
Implementacin de un algoritmo (Algorithm implementation) : Transcripcin a un lenguaje de programacin de un algoritmo. Ejemplo. Implementacin del algoritmo para el clculo de la media (para 4 valores) en C++:
Un programa incluir la implementacin de uno o ms algoritmos. Ejemplo. Programa para dibujar planos de pisos. Utilizar algoritmos para dibujar cuadrados, de medias aritmticas, salidas grcas en plotter, etc. Muchos de los programas que se vern en FP implementarn un nico algoritmo.
10
Ampliacin:
Uno de los pilares fundamentales de la informtica lo constituye la mquina de Turing (Turing machine) . Es un modelo formal de ordenador basado en un alfabeto, un conjunto de estados y un conjunto de transiciones sobre dichos estados.
La mquina de Turing permite realizar cualquier cmputo que un computador digital sea capaz de realizar Turing demostr que existen problemas que una mquina no puede resolver. Antes de disear la mquina de Turing, ste ayud a descifrar los mensajes encriptados por la mquina Enigma en la segunda guerra mundial. Consultad en Wikipedia la biografa de Turing. Tambin puede consultarse:
http://www.zator.com/Cpp/E0_1_1.htm
La importancia de Turing en el campo de la Informtica es tal que el equivalente a los premios Nobel llevan su nombre: A.M.Turing Award, patrocinado por Intel y Google. Por cierto, el servidor de la ETS Informtica se llama turing.ugr.es
11
I.1.4.
Compilacin
Al cdigo escrito en un lenguaje concreto se le denomina cdigo fuente (source code) . ste se guarda en cheros de texto normales (en el caso de C++ tienen extensin .cpp).
Pitagoras.cpp
/* Programa simple para el clculo de la hipotenusa de un tringulo rectngulo, aplicando el teorema de Pitgoras */ #include <iostream> // Inclusin de los recursos de E/S #include <cmath> // Inclusin de los recursos matemticos using namespace std; int main(){ double lado1; double lado2; double hipotenusa; // Programa Principal // Declara variables para guardar // los dos lados y la hipotenusa
cout << "Introduzca la longitud del primer cateto: " ; cin >> lado1; cout << "Introduzca la longitud del segundo cateto: "; cin >> lado2; hipotenusa = sqrt(lado1*lado1 + lado2*lado2); cout << "\nLa hipotenusa vale " << hipotenusa << "\n\n" ; }
12
Pitagoras.cpp es un simple chero de texto. Para obtener el programa ejecutable (el chero en binario que puede ejecutarse en un ordenador) se utiliza un compilador:
Cdigo fuente del programa Programa ejecutable
Compilacin
#include <iostream> using namespace std; int main() { double lado1; .......... cout << "Introduzca la longitud ... cin >> lado1; .......... }
Programa Ejecutable: Pitagoras.exe
Compilador
13
I.2.
I.2.1.
Especicacin de programas
Escritura y organizacin de un programa
Los programas en C++ pueden dividirse en varios cheros aunque por ahora vamos a suponer que cada programa est escrito en un nico chero (Pitagoras.cpp). Se pueden incluir comentarios en lenguaje natural.
14
Cuando llega a la llave cerrada } correspondiente a main(), y si no han habido problemas, el programa termina de ejecutarse y el Sistema Operativo libera los recursos asignados a dicho programa.
Existen varios tipos de sentencias: Sentencias de declaracin de datos (data) Cada dato que usemos en un programa debe declararse al principio (despus de main), asocindole un tipo de dato concreto.
15
Sentencias de Entrada/Salida Sentencias para mostrar informacin en el perifrico de salida establecido por defecto. En FP, dicho perifrico ser la pantalla, pero podra ser un chero en disco o dispositivo externo (impresora, plotter, puerto FireWire, etc.). Se construyen usando cout, que es un recurso externo incluido en la biblioteca iostream.
cout << "\nIntroduzca la longitud del primer cateto: "; ..... hipotenusa = sqrt(lado1*lado1 + lado2*lado2); cout << "\nLa hipotenusa vale ": cout << hipotenusa;
Observad el espacio al nal del mensaje. Qu hara lo siguiente? cout << "hipotenusa"; Podemos usar una nica sentencia: cout << "\nLa hipotenusa vale " << hipotenusa;
16
// MAL: totalVentas = 45; numeroVentas = 78; cout << totalVentas << numeroVentas;
// Imprime 4578
// BIEN: cout << "\nSuma total de ventas = " << totalVentas; cout << "\nNmero total de ventas = " << numeroVentas;
17
Sentencias para leer datos desde el dispositivo de entrada establecido por defecto. Por ahora, ser el teclado. Se construyen usando cin, que es un recurso externo incluido en la biblioteca iostream.
La lectura de datos con cin puede considerarse como una asignacin en tiempo de ejecucin
// MAL: cin >> lado1; cin >> lado2; // BIEN: cout << "Introduzca la longitud del primer cateto: "; cin >> lado1; cout << "Introduzca la longitud del segundo cateto: "; cin >> lado2;
Cuando la entrada se realiza desde un chero (y no el teclado), la lectura es automtica sin que se espere la tecla Intro.
18
Estructura bsica de un programa (los corchetes delimitan secciones opcionales): [ /* Breve descripcin en lenguaje natural de lo que hace el programa */ ]
19
I.2.2.
I.2.2.1.
Cada lenguaje de programacin tiene una sintaxis propia que debe respetarse para poder escribir un programa. Bsicamente, queda denida por: a) Los componentes lxicos (tokens) . Formados por caracteres alfanumricos y/o simblicos. Representan la unidad lxica mnima que el lenguaje entiende. main ; ( == = hipotenusa * Pero por ejemplo, ni ! ni ((* son tokens vlidos. b) Reglas sintcticas (Syntactic rules) : determinan cmo han de combinarse los tokens para formar sentencias. Algunas se especican con tokens especiales (formados usualmente por smbolos): Separador de sentencias ; Para agrupar varias sentencias se usa { }
Se ver su uso en el tema II. Por ahora, sirve para agrupar las sentencias que hay en el programa principal
20
I.2.2.2.
Palabras reservadas
Suelen ser tokens formados por caracteres alfabticos. Tienen un signicado especco para el compilador, y por tanto, el programador no puede denir variables con el mismo identicador. Algunos usos:
main
Para denir tipos de datos como por ejemplo double Para establecer el ujo de control (control ow) , es decir, para especicar el orden en el que se han de ejecutar las sentencias, como if, while, for etc. Estos se vern en el tema 2.
//
Errores sintcticos
int main(){ double main; // main es un nombre de dato incorrecto double double; // double es un nombre de dato incorrecto }
21
struct switch
unsigned void
volatile while
22
I.2.3.
Errores en tiempo de compilacin (Compilation error) Ocasionados por un fallo de sintaxis en el cdigo fuente. No se genera el programa ejecutable.
/* CONTIENE ERRORES #include <iostre am> USING namespace std; int main{}( double lado1: double lado 2, double hipotenusa:
*/
lado1 = 2; lado2 = 3 hipotenusa = sqrt(lado1**lado1 + ladp2*ladp2); cout << "La hipotenusa vale << hipotenusa; )
23
"Software and cathedrals are much the same. First we build them, then we pray".
Errores en tiempo de ejecucin (Execution error) Se ha generado el programa ejecutable, pero se produce un error durante la ejecucin.
24
I.3.
I.3.1.
Un programa realiza instrucciones y opera con datos. Una vez compilado el programa, todo son combinaciones adecuadas de 0 y 1.
MEMORIA MASIVA datos e instrucciones
MEMORIA PRINCIPAL 0101100 11110110 00000001 10100001 0001111 11100011 10100100 10111001 1111111 00111111 11010010 00100100 instrucciones datos
SALIDA datos
UNIDAD DE CONTROL
UNIDAD ARITMETICO-LOGICA
CPU
25
Datos
"Juan Prez" 1 0 1 1 .. 0 1 1
75225813
1 1 0 1 .. 0 0 0
3.14159
0 0 0 1 .. 1 1 1
Instrucciones
Abrir Fichero 0 0 0 1 .. 1 1 1
1 1 0 1 .. 0 0 0
26
I.3.2.
Al trabajar con un lenguaje de alto nivel, no haremos referencia a la secuencia de 0 y 1 que codican un valor concreto, sino a lo que representa para nosotros. Un dato (data) es un conjunto de celdas de memoria que tiene asociado un nombre -identicador (name) - y un contenido -valor (value) -.
NombreEmpleado NumeroHabitantes Pi
3.14159
Un programa necesitar representar datos de diverso tipo (cadenas de caracteres, enteros, reales, etc). Para cada tipo, el lenguaje ofrece un tipo de dato (data type) .
string NombreEmpleado; long NumeroHabitantes; double Pi; NombreEmpleado = "Juan Prez"; NumeroHabitantes = 75225813; Pi = 3.14159;
27
Por ejemplo, en C++: Enteros : int, long, ... Numricos Reales : float, double, ... Caracteres : char Simples Lgico : bool Puntero : * Enumerado : enum Vectores y matrices Cadena de caracteres : Vectores de caracteres, Compuestos string Registros Clases Por ahora slo usaremos double, int, char y string.
Tipos
28
I.3.2.1.
Literales
Son la especicacin de un valor concreto de un tipo de dato. Dependiendo del tipo, tenemos: Literales numricos (numeric literals) : son tokens numricos. Para representar datos reales, se usa el punto . para especicar la parte decimal: 2 3 3.5 Literales de cadenas de caracteres (string literals) : Son cero o ms caracteres encerrados entre comillas dobles: "Hola" Literales de otros tipos, como literales de caracteres (character literals) 'a', Literales lgicos (boolean literals) true, etc. I.3.2.2. Declaracin de variables
Cada variable que se use en un programa debe declararse al principio (despus de main), asocindole un tipo de dato concreto y un identicador. Al declarar una variable, el compilador reserva una zona de memoria para trabajar con ella. Ninguna otra variable podr usar dicha zona. Cada variable debe estar asociado a un nico tipo de dato (el tipo no puede cambiarse durante la ejecucin). El valor que se le puede asignar a una variable depende del tipo de dato con el que es declarado.
29
<tipo> <identificador1>; <tipo> <identificador2>; ... double lado1; double lado2; double hipotenusa;
Varios identicadores por declaracin:
<tipo> <identificador> = <valor_inicial>; <tipo> <identificador> = <valor_inicial>, <identificador>...; double dato = 4.5; equivale a: double dato; dato = 4.5;
30
Cuando se declara una variable y no se inicializa, sta no tiene ningn valor asignado por defecto. El valor almacenado es indeterminado y puede variar de una ejecucin a otra. Lo representaremos grcamente por ?
/* Programa para calcular la retencin a aplicar en el sueldo de un empleado */ #include <iostream> using namespace std; int main(){ double salario_bruto; // Salario bruto, en euros double retencion; // Retencin a aplicar, en euros salario_bruto
?
retencion
?
cout << "Introduzca salario bruto: "; cin >> salario_bruto; retencion = salario_bruto * 0.18; cout << "Retencin a aplicar: " << retencion; } salario_bruto
32538.0
retencion
4229.94
31
int main(){ double salario_bruto; // Salario bruto, en euros double retencion; // Retencin a aplicar, en euros retencion = salario_bruto * 0.18; // :-(
Podramos estar interesados en usar variables a las que slo permitimos tomar un nico valor, jado de antemano. Es posible con una constante (constant) . Se declaran como sigue:
32
/* Programa que pide el radio de una circunferencia e imprime su longitud y el rea del crculo */ #include <iostream> using namespace std; int main() { const double PI = 3.1416; double area, radio, longitud; PI = 3.15; // <- Error de compilacin :-) cout << "Introduzca el valor del radio "; cin >> radio; area = PI * radio * radio; longitud = 2 * PI * radio; cout << "El rea vale " << area; cout << "La longitud vale " << longitud; }
Ventaja declaracin de constantes: Informacin al propio programador. Imposibilidad de cambiarlo por error (PI). Cdigo menos propenso a errores. Para cambiar el valor de PI, (por ejemplo por 3.1415927), slo hay que tocar en la lnea de declaracin de la constante.
33
/* Programa para calcular la retencin a aplicar en el sueldo de un empleado */ #include <iostream> int main(){ const double IRPF = 0.18; // Porcentaje gravamen fiscal double retencion; // Retencin a aplicar, en euros double salario_bruto; // Salario bruto, en euros
salario_bruto
?
IRPF
0.18
retencion
?
cout << "Introduzca salario bruto: "; cin >> salario_bruto; retencion = salario_bruto * IRPF; cout << "\n Retencin a aplicar: " << retencion; }
salario_bruto
32538
IRPF
0.18
retencion
4229.94
34
Formalmente, al conjunto de variables y constantes, se le denomina datos. As pues, los datos se pueden clasicar como: variables datos constantes Las variables son datos cuyo valor puede variar a lo largo del programa, p.e., lado1. Las constantes son datos cuyo contenido no vara a lo largo de la ejecucin del programa, p.e., el nmero .
Una sintaxis de programa algo ms completa: [ /* Breve descripcin en lenguaje natural de lo que hace el programa */ ]
35
I.3.2.4.
Cada dato necesita un identicador nico. Un identicador de un dato es un token formado por caracteres alfanumricos con las siguientes restricciones: Debe empezar por una letra o subrayado (_) No pueden contener espacios en blanco ni ciertos caracteres especiales como letras acentuadas, la letra ee, las barras \ o /, etc. Ejemplo: lado1 lado2 precio_con_IVA El compilador determina la mxima longitud que pueden tener (por ejemplo, 31 caracteres) Sensibles a maysculas y minsculas. lado y Lado son dos identicadores distintos. No se podr dar a un dato el nombre de una palabra reservada. No es recomendable usar el nombre de algn identicador usado en las bibliotecas estndar (por ejemplo, cout)
#include <iostream> using namespace std; int main(){ double cout; // Error de sintaxis double main; // Error de sintaxis
Ejercicio. Determinar cules de los siguientes son identicadores vlidos. Si son invlidos explicar por qu. a) registro1 b) 1registro c) archivo_3 d) main
e) nombre y direccion
f) direccin
g) diseo
36
Consejos muy importantes para elegir el identicador de un dato: El identicador de un dato debe reejar su semntica (contenido). Por eso, salvo excepciones (como las variables contadoras de los bucles -tema II-) no utilizaremos nombres con pocos caracteres
37
38
I.4.
I.4.1.
Operadores y expresiones
Terminologa en Matemticas
Notaciones usadas en los operadores matemticos: Notacin preja (Prex notation) . El operador va antes de los argumentos. Estos suelen encerrarse entre parntesis. seno(3), tangente(x), media(valor1, valor2) Notacin inja (Inx notation) . El operador va entre los argumentos. 3+5 x/y Segn el nmero de argumentos, diremos que un operador es: Operador unario (Unary operator) . Slo tiene un argumento: seno(3), tangente(x) Operador binario (Binary operator) . Tienes dos argumentos: media(valor1, valor2) 3+5 x/y Operador n-ario (n-ary operator) . Tiene ms de dos argumentos.
39
I.4.2.
Operadores en Programacin
Los lenguajes de programacin proporcionan operadores que permiten manipular los datos. Se denotan a travs de tokens alfanumricos o simblicos. Suelen devolver un valor y actan sobre ciertos datos (variables o constantes), literales y sobre el resultado que dan otros operadores. Tipos de operadores: Los denidos en el ncleo del compilador. No hay que incluir ninguna biblioteca Suelen usarse tokens simblicos para su representacin y suelen ser injos Ejemplos: + (suma), - (resta), * (producto), etc. Los denidos en bibliotecas externas. Por ejemplo, cmath Suelen usarse tokens alfanumricos para su representacin y suelen ser prejos. Si hay varios argumentos se separan por una coma.
sqrt(4)
sin(6.4)
pow(3,6)
Tradicionalmente se usa el trmino operador (operator) a secas para denotar los primeros, y el trmino funcin (function) para los segundos. A los argumentos de las funciones se les denomina parmetros (parameter) .
40
int main() { const double PI = 3.1415926; double area, radio; cout << "Introduzca el valor del radio "; cin >> radio; // Asignaciones vlidas: area = PI * pow(radio, 2); area = PI * radio * radio;
En general:
variable = Expresin;
Primero se evala la expresin en la parte derecha de la asignacin y luego se realiza la asignacin.
41
I.4.3.
Expresiones
Una expresin (expression) es una combinacin de datos y operadores sintcticamente correcta, que el compilador evala y devuelve un valor.
42
Cuando el compilador evala una expresin, devuelve un valor de un tipo de dato (entero, real, carcter, etc.). Diremos que la expresin es de dicho tipo de dato. Por ejemplo:
3 + 5 es una expresin entera 3.5 + 6.7 es una expresin de reales int main(){ double dato_real; int dato_entero; dato_real = 3.5 + 6.7; datos_entero = 3 + 5; ...... }
Nota. Cuando se usa una expresin dentro de cout, el compilador detecta el tipo de dato resultante y la imprime de forma adecuada.
cout << "\nResultado = " << 3+5; cout << "\nResultado = " << 3.5+6.7;
Imprime en pantalla:
43
I.5.
I.5.1.
El comportamiento de un tipo de dato viene dado por: El rango (range) de valores que puede representar, que depende de la cantidad de memoria que dedique el compilador a su representacin interna. Intuitivamente, cuanta ms memoria se dedique para un tipo de dato, mayor ser el nmero de valores que podremos representar. El conjunto de operadores que pueden aplicarse a los datos de ese tipo. Ampliacin:
La cantidad de memoria que el compilador asigna a cada tipo se puede consultar con el operador sizeof(<tipo>) cuyo resultado es un entero que contiene el nmero de bytes asignados a <tipo>. sizeof es un operador pues est en el ncleo del compilador, pero se usa en forma preja, encerrando el argumento entre parntesis, como las funciones. Los valores mnimos y mximos que se pueden representar para cada tipo, se pueden consultar en los cheros climits y cfloat. Consultad tambin:
http://msdn2.microsoft.com/en-us/library/d4zh6te4.aspx
Introduccin a la Programacin Tipos de datos comunes en C++ Los tipos de datos enteros
44
I.5.2.
I.5.2.1.
Propiedad fundamental: Cualquier entero puede descomponerse como la suma de determinadas potencias de 2. 53 = 0 215 +0 214 +0 213 +0 212 +0 211 +0 210 +0 29 +0 28 +0 27 + +0 26 + 1 25 + 1 24 + 0 23 + 1 22 + 0 21 + 1 20 La representacin en binario sera la secuencia de los factores (1,0) que acompaan a las potencias: 0000000000110101 Dos elementos a combinar: 1, 0 r posiciones. Por ejemplo, r = 16 Se permiten repeticiones e importa el orden 0000000000110101 = 0000000000110110 Nmero de datos distintos representables = 2r Se conoce como bit a la aparicin de un valor 0 o 1. Un byte es una secuencia de 8 bits. Ampliacin:
La forma fcil de representar el signo sera aadiendo otro bit al principio para indicar si es positivo o negativo (bit de signo). Los ordenadores actuales usan otras tcnicas que agilizan los cmputos. Buscar en Internet: Representacin de nmeros con signo
Introduccin a la Programacin Tipos de datos comunes en C++ Los tipos de datos enteros
45
I.5.2.2.
El rango de un deenterointeger es un subconjunto del conjunto matemtico Z . La cardinalidad depender del nmero de bits (r ) que cada compilador utiliza para su almacenamiento. Los compiladores suelen ofrecer distintos tipos enteros. En C++: short, int, long, __int64, etc. El ms usado es int. El estndar de C++ no obliga a los compiladores a usar un tamao determinado. Lo usual es que un int ocupe la longitud de la palabra (word) del segmento de datos del sistema operativo. Hoy en da, lo usual es 32 bits (8 bytes) y 64 bits (16 bytes). En el caso de 32 bits el rango de un int es: 232 232 , 1 = [2147483648, 2147483647] 2 2
Introduccin a la Programacin Tipos de datos comunes en C++ Los tipos de datos enteros
46
I.5.2.3.
Literales enteros
Un literal (literal) es la especicacin (dentro del cdigo fuente) de un valor concreto de un tipo de dato. Los literales enteros (integer literals) se construyen con tokens formados por smbolos numricos. Pueden empezar con un signo -
53
-406778
Nota. En el cdigo se usa el sistema decimal (53) pero internamente, el ordenador usa el cdigo binario (000000000110101) Qu tipo de dato se usa para representar un literal entero? Depende de la memoria reservada y de los tipos disponibles en cada compilador. El compilador usar el tipo de dato que se ajuste al tamao del literal entero. As, si un compilador usa 32 bits para un int, 600000000 ser un literal de tipo int.
int entero; entero = 600000000; // int = int __int64 gran_entero; gran_entero = 600000000000000; // __int64 = __int64
La asignacin en el cdigo de un literal demasiado grande provocar un error de compilacin:
Introduccin a la Programacin Tipos de datos comunes en C++ Los tipos de datos enteros
47
I.5.2.4.
Operadores
Operadores binarios
suma, resta, producto, divisin entera y mdulo. El operador mdulo (%) representa el resto de la divisin entera Devuelven un entero. Binarios. Notacin inja: a*b
int n = n = n = n = n = n = n = n = n = n = 5 /
// // // // // // // // // // //
Asigna a la variable n Asigna a la variable n Asigna a la variable n Asigna a la variable n Asigna a la variable n Asigna a la variable n Asigna a la variable n Asigna a la variable n Asigna a la variable n Asigna a la variable n Sentencia Incorrecta.
el el el el el el el el el el
valor valor valor valor valor valor valor valor valor valor
35 36 2 7 0 1 17 5 2 3
Operaciones usuales: Extraer el dgito menos signicativo: 5734 % 10 4 Truncar desde el dgito menos signicativo: 5734 / 10 573
Introduccin a la Programacin Tipos de datos comunes en C++ Los tipos de datos enteros
48
++ y --
Incrementan y decrementan, respectivamente, el valor de la variable entera sobre la que se aplican (no pueden aplicarse sobre una expresin).
<variable>++;
<variable>--;
/* Incrementa la variable en Es equivalente a: <variable> = <variable> + /* Decrementa la variable en Es equivalente a: <variable> = <variable> // Asigna 5 a dato // Asigna 6 a dato
1 1; */ 1 1; */
Introduccin a la Programacin Tipos de datos comunes en C++ Los tipos de datos enteros
49
I.5.2.5.
Expresiones enteras
entera
56
(entera/4+56)%3
3 + 5 * 7;
Posibilidad 1: 1. Primero hacer 3+5 2. Despus, el resultado se multiplica por 7 El resultado sera: 56 Posibilidad 2: 1. Primero hacer 5*7 2. Despus, el resultado se suma a 3 El resultado sera: 38 La forma que adopta el compilador depende de la precedencia de los operadores. Ante la duda, forzad la evaluacin deseada mediante la utilizacin de parntesis:
dato = 3+(5*7);
dato = (3+5)*7;
Introduccin a la Programacin Tipos de datos comunes en C++ Los tipos de datos enteros
50
Reglas de precedencia:
() - (operador unario de cambio de signo) * / % + Cualquier operador de una la superior tiene ms prioridad que cualquiera de la la inferior.
variable = 3+5*7;
// equivale a 3+(5*7)
Los operadores de una misma la tienen la misma prioridad. Se evalan de izquierda a derecha segn aparecen en una expresin.
variable = 3/5*7;
// equivale a (3/5)*7
Ejercicio. Teniendo en cuenta el orden de precedencia de los operadores, indicad el orden en el que se evaluaran las siguientes expresiones: a) a + b * c -d b) a * b / c c) a * c % b - d
Ejemplo. Incrementar el salario en 100 euros y calcular el nmero de billetes de 500 euros a usar en el pago.
int salario, num_bill500; salario = 43000; num_bill500 = (salario + 100) / 500; // ok num_bill500 = salario + 100 / 500; // Error lgico
Introduccin a la Programacin Tipos de datos comunes en C++ Los tipos de datos enteros
51
I.5.2.6.
int entero_normal; __int64 entero_grande, otro_entero_grande; entero_normal = 45; entero_grande = 600000000000000; otro_entero_grande = entero_normal * entero_grande;
En la expresin
entero_normal * entero_grande
usamos dos tipos enteros distintos.
#
"
Para evaluar el resultado de una expresin que contenga datos con tipos distintos, el compilador usar el tipo adecuado que sea capaz de albergar el resultado.
Para albergar el producto de un entero normal y otro grande, necesita un tipo entero grande. Hecho el cmputo, ya puede asignar el resultado a la variable otro_entero_grande. Recordad: Primero, se evala la expresin (usando el tipo adecuado) Segundo, se realiza la asignacin
Introduccin a la Programacin Tipos de datos comunes en C++ Los tipos de datos enteros
52
I.5.2.7.
Problemas de desbordamiento
Recordemos que con un literal demasiado grande, el compilador da un error al intentar asignarlo a una variable. Pero al trabajar con datos arbitrarios el compilador no puede realizar ninguna comprobacin. Por tanto, es fcil salirse del rango representable. En este caso, se produce un desbordamiento aritmtico (arithmetic overow) y el resultado es un valor impredecible. Se produce un error lgico.
int entero, incremento; __int64 entero_grande; entero = 600000000000; // Error Compilacin :-) entero = 600000000; // Correcto. "Le cabe" incremento = 1000; entero_grande = entero * incremento // Correcto: __int64 = __int64 entero = entero * incremento; // Desbordamiento:
El compilador es capaz de evaluar la expresin
int = __int64
Introduccin a la Programacin Tipos de datos comunes en C++ Los tipos de datos enteros
53
I.5.2.8.
Una de las principales crticas al estndar C++ es que cada compilador tiene cierto margen de maniobra y puede implementar cada tipo de dato de forma distinta. Una restriccin fundamental es:
1 byte
==
char <=
short <=
int
<= long
Por tanto, podra ser que un int fuese un entero de 32 bits con signo en un compilador y en otro fuese un entero de 16 bits con signo. Obviamente, esto puede ocasionar problemas de desbordamiento como hemos visto en la seccin anterior. Los cheros de cabecera <climits> y <float.h> contienen deniciones de los rangos de valor de todos los tipos fundamentales. Ampliacin:
Consultad las restricciones bsicas en:
http://www.zator.com/Cpp/E2_2_4c.htm
y una referencia ms avanzada en:
http://www.zator.com/Cpp/E2_2_4.htm
Introduccin a la Programacin Tipos de datos comunes en C++ Los tipos de datos reales
54
I.5.3.
Un dato de tipo real (oat) tiene como rango un subconjunto nito de R Parte entera de 4,56 4 Parte real de 4,56 56 C++ ofrece distintos tipos para representar valores reales. Principalmente, float (usualmente 32 bits) y double (usualmente 64 bits).
Son tokens formados por dgitos numricos y con un nico punto que separa la parte decimal de la real. Pueden llevar el signo - al principio.
800.457
Importante:
4.0
-3444.5
El literal 3 es un entero. El literal 3.0 es un real. Los compiladores suelen usar el tipo double para representar literales reales. Tambin se puede utilizar notacin cientca:
5.32e+5 representa el nmero 5,32 105 = 532000 42.9e-2 representa el nmero 42,9 102 = 0,429
Introduccin a la Programacin Tipos de datos comunes en C++ Los tipos de datos reales
55
I.5.3.2.
Cmo podra el ordenador representar 541,341? Lo fcil sera: Representar la parte entera 541 en binario Representar la parte real 341 en binario De esa forma, con 64 bits (32 bits para cada parte) podramos representar: Partes enteras en el rango [2147483648, 2147483647] Partes reales en el rango [2147483648, 2147483647] Sin embargo, la forma usual de representacin no es as. Se utiliza la representacin en coma otante (oating point) . La idea es representar un valor y la escala. En aritmtica decimal, la escala se mide con potencias de 10: 42,001 42001 valor = 4,2001 escala = 10 valor = 4,2001 escala = 104
0,42001 valor = 4,2001 escala = 101 El valor se denomina mantisa (mantissa) y el coeciente de la escala exponente (exponent) . En la representacin en coma otante, la escala es 2. A grosso modo se utilizan m bits para la mantisa y n bits para el exponente. La forma explcita de representacin en binario se ver en otras asignaturas. Podemos adelantar que la representacin de la parte real se hace en potencias inversas de 2. Por ejemplo, 1011 representara 1 1 21 +0 1 22 +1 1 23 +1 1 24 =
Introduccin a la Programacin Tipos de datos comunes en C++ Los tipos de datos reales
56
=1
1 2
+0
1 4
+1
1 8
+1
1 16
= 0,6875
Problema: Si bien un entero se puede representar de forma exacta como suma de potencias de dos, un real slo se puede aproximar con suma de potencias inversas de dos. Valores tan sencillos como 0,1 o 0,01 no se pueden representar de forma exacta. 0,1 1 1 24 +0 1 25 +0 1 26 +0 1 27 +0 1 28 +
Por tanto: Todas las operaciones realizadas con los reales pueden devolver valores que slo sean aproximados!
Introduccin a la Programacin Tipos de datos comunes en C++ Los tipos de datos reales
57
I.5.3.3.
Rango y Precisin
La codicacin en coma otante separa el valor de la escala. Esto permite trabajar (en un mismo tipo de dato) con magnitudes muy grandes y muy pequeas.
// ok // ok!
double valor_real; valor_real = 123456789012345.0; // :-) valor_real = 12345678901234567.0; // :-( // Se almacenar un valor aproximado. // Por ejemplo: 12345678901234563.0;
La regla a usar es que con 32 bits, se consigue una precisin aproximada de 7 dgitos y con 64 bits, se consiguen aproximadamente 16 dgitos.
Introduccin a la Programacin Tipos de datos comunes en C++ Los tipos de datos reales
58
valor_real = 0.12345678901234567; // Se almacenar un valor aproximado. Por ejemplo: // 0.12345678901234563; valor_real = 0.1; // Ya lo vimos antes // Tambin se almacenar un valor aproximado. // Por ejemplo: 0.99999899897
Cuanto mayor sea la parte entera a representar, menor ser la precisin en decimales que obtendremos (sin contar que, adems, muchos decimales como 0.1 no se pueden representar de forma exacta) Por ejemplo, usando 32 bits, hay aproximadamente 8388607 nmeros entre 1,0 y 2,0, mientras que slo hay 8191 entre 1023,0 y 1024,0 Debido a la poca precisin, los programas nancieros no trabajan con reales en coma otante. Soluciones: Trabajar con enteros y potencias de 10.
int salario; salario = 175023; // 1750 euros y 23 cntimos salario = 143507; // 1435 euros y 7 cntimos salario = 130000; // 1300 euros
Usar tipos o clases especcas (BCD por ejemplo)
Introduccin a la Programacin Tipos de datos comunes en C++ Los tipos de datos reales
59
En resumen:
' $
Los tipos enteros representan datos enteros de forma exacta. Los tipos reales representan la parte entera de forma exacta, siempre que trabajemos con menos de 16 dgitos (en una representacin con 64 bits) La parte real ser slo aproximada. Adems, la aproximacin ser mejor cuanto menor sea la parte entera del nmero.
&
Los reales en coma otante tambin permiten representar valores especiales como innito (innity) y una indeterminacin (undened) NaN (Not a Number)
double valor_real, divisor = 0.0; valor_real = 17.5 / divisor; // Almacena INF valor_real = 1.5 / valor_real; // Almacena 0.0 valor_real = divisor / divisor; // Almacena NaN valor_real = 1.5 / valor_real; // Almacena NaN
Introduccin a la Programacin Tipos de datos comunes en C++ Los tipos de datos reales
60
I.5.3.4.
Operadores
+, -, *, /
Binarios, de notacin inja. Tambin se puede usar el operador unario de cambio de signo (-). Aplicados sobre reales, devuelven un real.
Cuidado! El comportamiento del operador / depende del tipo de los operandos: si todos son enteros, es la divisin entera. Si todos son reales, es la divisin real.
// Asigna a real el valor 0.7142857 // Asigna a real el valor 0 // <- Lo vemos despus
Introduccin a la Programacin Tipos de datos comunes en C++ Los tipos de datos reales
61
I.5.3.5.
Funciones estndar
Hay algunas bibliotecas estndar que proporcionan funciones que trabajan sobre datos numricos (enteros o reales) y que suelen devolver un real. Por ejemplo, cmath
#include <iostream> #include <cmath> using namespace std; int main(){ double abscisa, ordenada; abscisa = 5.4; ordenada = sqrt(abscisa); ordenada = pow(4.3, abscisa); }
Introduccin a la Programacin Tipos de datos comunes en C++ Los tipos de datos reales
62
I.5.3.6.
Expresiones reales
sqrt(abscisa) es una expresin real pow(4.3, abscisa) es una expresin real sqrt(b*b - 4.0*a*c) es una expresin real
En general, diremos que las expresiones aritmticas (arithmetic expression) o numricas son las expresiones o bien enteras o bien reales. Precedencia de operadores:
Consejo:
&
Para facilitar la lectura de las frmulas matemticas, evitad el uso de parntesis cuando est claro cul es la precedencia de cada operador.
b +
b2 4ac
2a
Introduccin a la Programacin Tipos de datos comunes en C++ Los tipos de datos reales
63
Ejemplo. Calcular las races de una ecuacin de 2o grado. p(x) = ax2 + bx + c = 0 Algoritmo: Entradas: Los parmetros de la ecuacin a, b, c. Salidas: Las races de la parbola r 1, r 2 Descripcin: Calcular r 1, r 2 en la forma siguiente: b + b2 4ac b b2 4ac
r1 =
2a
r2 =
2a
p(x) = ax2 + bx + c
5 p(x)
-5
-10
-15
-20 -4 -2 0 2 4
Introduccin a la Programacin Tipos de datos comunes en C++ Los tipos de datos reales
64
#include <iostream> #include <cmath> using namespace std; int main(){ double a, b, c; // Parmetros de la ecuacin double raiz1, raiz2; // Races obtenidas cout << "\nIntroduce coeficiente de 2o grado: "; cin >> a; cout << "\nIntroduce coeficiente de 1er grado: "; cin >> b; cout << "\nIntroduce coeficiente independiente: "; cin >> c; raiz1 = ( -b + sqrt( b*b-4*a*c ) ) / (2*a); raiz2 = ( -b - sqrt( b*b-4*a*c ) ) / (2*a); cout << "\nLas races son" << raiz1 << " y " << raiz2; }
Cada descripcin de comportamiento debe aparecer una nica vez en nuestro programa.
Dicho de otra forma: JAMS ha de repetirse cdigo La repeticin de cdigo hace que los programas sean difciles de actualizar ya que cualquier cambio ha de realizarse en todos los sitios en los que est repetido el cdigo.
Introduccin a la Programacin Tipos de datos comunes en C++ Los tipos de datos reales
65
Para no repetir cdigo usamos una variable para almacenar el valor de la expresin que se repite:
#include <iostream> #include <cmath> using namespace std; int main(){ double a, b, c; // Parmetros de la ecuacin double raiz1, raiz2; // Races obtenidas double radical, denominador; cout << "\nIntroduce coeficiente de 2o grado: "; cin >> a; cout << "\nIntroduce coeficiente de 1er grado: "; cin >> b; cout << "\nIntroduce coeficiente independiente: "; cin >> c; denominador = 2*a; radical = sqrt( b*b-4*a*c ); raiz1 = (-b + radical) / denominador; raiz2 = (-b - radical) / denominador; cout << "\nLas races son" << raiz1 << " y " << raiz2; }
Introduccin a la Programacin Tipos de datos comunes en C++ Operando con tipos numricos distintos
66
I.5.4.
I.5.4.1.
* 7.0 / 7.0 7 / 7
Recordad que para evaluar el resultado de una expresin que contenga datos con tipos distintos, el compilador usar el tipo adecuado que sea capaz de albergar el resultado. Por ejemplo, usar un double para almacenar el resultado de 5.0 / 7, es decir, 0.7142857. El operador de asignacin tambin permite trabajar con tipos distintos.
double real; int entero = 5; real = 5; // double = int real = entero; // double = int
Internamente se produce una transformacin de tipo (casting) del valor 5 (en su representacin como un int) al valor 5.0 (en su representacin como un double)
Introduccin a la Programacin Tipos de datos comunes en C++ Operando con tipos numricos distintos
67
= a = a
Muy importante: Primero se evala la expresin en la parte derecha de la asignacin y luego se realiza la asignacin.
int edad1 = 10, edad2 = 5; double media; media = (edad1 + edad2)/2; // :-(
asigna a media el valor 7.0. Es un error lgico, difcil a veces de detectar. Posible solucin: Declarar edad1 y edad2 como double? El resultado sera 7.5, pero no debemos cambiar el tipo asociado a la edad, pues su semntica es de un entero. Forzaremos la divisin real usando un literal real en un operando.
int edad1 = 10, edad2 = 5; double media; media = (edad1 + edad2)/2.0; // :-)
Introduccin a la Programacin Tipos de datos comunes en C++ Operando con tipos numricos distintos
68
Qu pasa cuando asignamos un real a un entero? Simplemente, se pierde la parte decimal, es decir, se trunca.
double real; int entera; real = 5.7; entera = real // Asigna a entera el valor 5
Este truncamiento tambin ocurre si ejecutamos
Estudiar: Un caso especial es el casting a entero de un double que contenga innito o indeterminacin. El resultado es un valor impredecible.
int entero, denominador; double real; denominador = 0; real = 17/(denominador*1.0); entero = real; real = 0/(denominador*1.0); entero = real; entero = 17/denominador;
// // // // //
Almacena INF Almacena basura Almacena NaN Almacena basura Error ejecucin Por qu?
69
I.5.5.
I.5.5.1.
El rango de un dato de tipo carcter (char) es un conjunto nito y ordenado de caracteres: letras minsculas, maysculas, dgitos del 0 al 9 y otros caracteres especiales. Hay varios tipos de dato de carcter: char, wchar, signed char, etc. En FP usaremos char.
70
Cdigo ASCII extendido (256 caracteres) Cada carcter tiene asociado un nmero de orden (entre 0 y 255)
71
72
Literales de carcter Son tokens formados por: Un nico carcter encerrado entre comillas simples: '!' 'A' 'a' '5' '' Observad que '5' es un literal de carcter y 5 es un literal entero Cuidado!: 'cinco' o '11' no son literales de carcter. O bien una secuencia de escape, es decir, el smbolo \ seguido de otro smbolo, como por ejemplo: Secuencia Signicado
\n \t \b \r \f \' \" \\
Nueva lnea (retorno e inicio) Tabulador Retrocede 1 carcter Retorno de carro Salto de pgina Comilla simple Comilla doble Barra inclinada
Las secuencias de escape tambin deben ir entre comillas simples, por ejemplo, '\n', '\t', etc.
73
#include <iostream> using namespace std; int main(){ const char NUEVA_LINEA = '\n'; char letra_piso; letra_piso = 'B'; // Almacena 'B' cout << letra_piso << "ienvenidos"; cout << '\n' << "Empiezo a escribir en la siguiente lnea"; cout << '\n' << '\t' << "Acabo de tabular esta lnea"; cout << NUEVA_LINEA; cout << '\n' << "Esto es una comilla simple: " << '\''; }
Escribira en pantalla:
Bienvenidos Empiezo a escribir en la siguiente lnea Acabo de tabular esta lnea Esto es una comilla simple: '
Nota. Para escribir un retorno de carro, tambin puede usarse una constante llamada endl en la forma: cout << endl << "Adis" << endl
Observad la diferencia:
double r; char letra; letra = 'r'; r = 23; // literal de carcter 'r' // variable real r
74
I.5.5.2.
Funciones estndar
El chero de cabecera cctype contiene varias funciones relativas a caracteres. Por ejemplo:
tolower #include <cctype> using namespace std; int main(){ char letra_piso; letra_piso = tolower('A'); letra_piso = toupper('A'); letra_piso = tolower('B'); letra_piso = tolower('?'); ..................
toupper
// // // //
75
I.5.5.3.
// letra_piso contiene 65
// letra_piso contiene 65
// // // //
65 65 55 7
As pues, el tipo char es un entero pequeo ya que est pensado para albergar los nmeros de rdenes de la tabla ASCII (entre 0 y 255)
76
Veamos algunas consecuencias: Podemos operar con los caracteres como si fuesen nmeros:
char letra_piso; letra_piso = 'A'+1; letra_piso = 65+1; letra_piso = '7'-1; letra_piso = 'A' + '7';
// // // // //
Tambin valdra int letra_piso; Almacena 66 = 'B' Almacena 66 = 'B' Almacena 54 = '6' Almacena 120
Tambin podemos usar el operador - con dos argumentos de carcter. Devuelve un entero:
El recurso cout se ha programado de la siguiente forma: Si se pasa a cout un literal o variable de tipo char, imprime el carcter correspondiente al entero almacenado. Si se pasa a cout un literal o variable de tipo entero (distinto a char), imprime el entero almacenado. Qu imprimira la sentencia cout << 'A'+1? No imprime 'B' como cabra esperar sino 66 ya que 1 es un literal entero y por tanto de tipo int. El tipo int es ms grande que el tipo char, por lo que 'A'+1 es una expresin de tipo int.
77
Estudiar: C++ ofrece un amplio repertorio de tipos de datos para representar, por ejemplo, enteros slo positivos, reales muy grandes, etc. Consultad
http://www.zator.com/Cpp/E2_2_3.htm http://www.cplusplus.com/doc/tutorial/variables/
Introduccin a la Programacin Tipos de datos comunes en C++ El tipo de dato cadena de caracteres
78
I.5.6.
Un literal de tipo cadena de caracteres (string) es una sucesin de caracteres encerrados entre comillas dobles: "Hola", "a" son literales de cadena de caracteres
int main(){ cout << "Bienvenidos"; cout << "\nEmpiezo a escribir en la siguiente lnea"; cout << "\n\tAcabo de tabular esta lnea"; cout << "\n"; cout << "\nEsto es una comilla simple '"; cout << " y esto es una comilla doble \""; }
Escribira en pantalla:
Bienvenidos Empiezo a escribir en la siguiente lnea Acabo de tabular esta lnea Esto es una comilla simple ' y esto es una comilla doble "
Introduccin a la Programacin Tipos de datos comunes en C++ El tipo de dato cadena de caracteres
79
Ejercicio. Determinar cuales de las siguientes son constantes de cadena de caracteres vlidas, y determinar la salida que tendra si se pasase como argumento a cout a) "8:15 P.M." b) "'8:15 P.M." c) '"8:15 P.M."' f) "Direccin\'n"
C++ ofrece dos alternativas para trabajar con cadenas de caracteres: Cadenas estilo C: son vectores de caracteres con terminador '\0'. Se ver en la asignatura Metodologa de la Programacin. Usando el tipo string (la recomendada en esta asignatura)
int main(){ string mensaje_bienvenida; mensaje_bienvenida = "\tFundamentos de Programacin\n"; cout << mensaje_bienvenida; }
Para poder operar con un string debemos incluir la biblioteca string:
#include <iostream> #include <string> using namespace std; int main(){ string cad; cad = "Hola y "; cad = cad + "adis"; cout << cad; }
Introduccin a la Programacin Tipos de datos comunes en C++ El tipo de dato cadena de caracteres
80
Realmente, string es una clase (lo veremos en temas posteriores) por lo que le sern aplicables mtodos en la forma:
// :-O
$
&
La lectura de un string con cin es complicada cuando la cadena contiene separadores (espacios en blanco, tabuladores, etc). Por lo tanto, en esta asignatura, asumiremos que las cadenas ledas con cin no contienen separadores.
Introduccin a la Programacin Tipos de datos comunes en C++ El tipo de dato lgico o booleano
81
I.5.7.
Es un tipo de dato muy comn en los lenguajes de programacin que se utiliza para representar los valores verdadero y falso que suelen estar asociados a una condicin. En C++ se usa el tipo bool. I.5.7.1. Rango
El rango de un dato de tipo lgico (boolean) est formado solamente por dos valores: verdadero y falso. Para representarlos, se usan los siguientes literales:
true
false
Una expresin lgica es una expresin cuyo resultado es un tipo de dato lgico. I.5.7.2. Operadores
Son los operadores clsicos de la lgica Y, O, NO que en C++ son los operadores &&, ||, ! respectivamente.
Introduccin a la Programacin Tipos de datos comunes en C++ El tipo de dato lgico o booleano
82
p Carlos es varn q Carlos es joven p true true q true p && q p || q true true true true false true false
!p
false true
false true
false false false Por ejemplo: Si p es false, y q es true, p && q ser false p || q ser true Recordad:
Introduccin a la Programacin Tipos de datos comunes en C++ El tipo de dato lgico o booleano
83
isalpha
Por ejemplo,
isalnum
isdigit
...
isalpha('3')
es una expresin lgica (devuelve false)
/* Ejemplo de tipo de dato lgico (bool) #include <cctype> using namespace std;
*/
int main() { bool es_alfa, es_alfanum, es_digito_numerico; bool compuesto; es_alfa = isalpha('3'); // Asigna false a es_alfa es_alfanum = isalnum('3'); // Asigna true a es_alfanum es_digito_numerico = isdigit('3'); // Asigna true a es_digito_numerico compuesto = (es_alfa && es_alfanum); compuesto = (es_alfa || es_alfanum); compuesto = !es_alfa; ....... } // Resultado: false // Resultado: true // Resultado: true
Introduccin a la Programacin Tipos de datos comunes en C++ El tipo de dato lgico o booleano
84
I.5.7.3.
Operadores Relacionales
Son los operadores habituales de comparacin de expresiones numricas. Pueden aplicarse a operandos tanto enteros, reales, como de caracteres y tienen el mismo sentido que en Matemticas. El resultado es de tipo bool.
== (igual), != (distinto), <, >, <= (menor o igual) y >= (mayor o igual)
Algunos ejemplos: La expresin (4 < 5) devuelve valor true La expresin (4 > 5) devuelve el valor false La relacin de orden entre caracteres se establece segn la tabla ASCII. La expresin ('a' > 'b') devuelve el valor false.
!= es el operador relacional distinto. ! es la negacin lgica. == es el operador relacional de igualdad. = es la operacin de asignacin.
Tanto en == como en != se usan 2 signos para un nico operador
Introduccin a la Programacin Tipos de datos comunes en C++ El tipo de dato lgico o booleano
85
//
int main(){ int entero1 = 3, entero2 = 5; double real1, real2; bool menor, iguales; menor = menor = menor = real1 = real2 = menor = menor = iguales }
Veremos su uso en la sentencia condicional:
entero1 < entero2; entero2 < entero1; (entero1 < entero2) && !(entero2 < 7); 3.8; 8.1; real1 > entero1; !menor && (real1 > real2); = real1 == real2;
if (4 < 5) cout << "4 es menor que 5"; if (!(4 > 5)) cout << "4 es menor o igual que 5";
Introduccin a la Programacin Tipos de datos comunes en C++ El tipo de dato lgico o booleano
86
Reglas de Precedencia:
int A = 40, B = 34, C = 50; bool condicion; condicion = A <= B && !B > C; condicion = (A <= B) && (!(B > C)); condicion = (A <= B) && !(B > C); condicion = (A <= B) && (B <= C);
// // // //
Consejo:
Ejercicio. Escribid una expresin lgica que devuelva true si un nmero entero edad est en el intervalo [0,100]
87
I.5.8.
El tipo enumerado
Supongamos un programa de contabilidad en el que queremos representar tres posibles valores de desgravacin, a saber 7, 16 y 30 %. Alternativas: Usar una nica variable desgravacion
const int desgravacion_7 = 7; const int desgravacion_16 = 16; const int desgravacion_33 = 33;
Tedioso, pues siempre tendremos que manejar tres variables
88
Usar un tipo enumerado (enumerated type) . Es un tipo entero que slo acepta unos valores concretos especicados por el programador.
double salario; enum TipoDesgravacion {baja=7, media=16, alta=33}; TipoDesgravacion desgravacion; cout << baja; // Imprime 7 desgravacion = baja; // Almacena internamente 7 cin >> salario; salario = salario - salario*desgravacion/100.0; desgravacion = 5; cin >> desgravacion;
#
"
Si a una constante slo se le puede asignar un nico valor, a un tipo enumerado slo se le puede asignar un nmero muy limitado de valores.
Ampliacin:
Si no se pone ningn valor en la denicin del tipo, C++ le asigna a cada valor del enumerado el siguiente entero del asignado al valor anterior. Al primero le asigna el cero.
enum TipoPocoUsual {baja, media, alta=4, muy_alta}; TipoPocoUsual raro; raro = baja; cout << raro; // 0 cout << media; // 1 cout << alta; // 4 cout << muy_alta; // 5
89
Bibliografa recomendada para este tema: A un nivel menor del presentado en las transparencias: Primer captulo de Deitel & Deitel Primer captulo de Garrido. A un nivel similar al presentado en las transparencias: Captulo 1 y apartados 2.1, 2.2 y 2.3 de Savitch A un nivel con ms detalles: Los tres primeros captulos de Stephen Prata. Los dos primeros captulos de Lafore.
Tema II
Estructuras de Control
Objetivos: Introducir las estructuras condicionales que nos permitirn realizar saltos hacia adelante durante la ejecucin del cdigo. Introducir las estructuras repetitivas que nos permitirn realizar saltos hacia atrs durante la ejecucin del cdigo. Introducir pautas de programacin en la construccin de las estructuras condicionales y repetitivas.
Autor: Juan Carlos Cubero. Sugerencias: por favor, enviar un e-mail a JC.Cubero@decsai.ugr.es
91
II.1.
Estructura condicional
Secuencial Estructuras Condicional Repetitiva
Estructura secuencial (sequential control ow structure) : las instrucciones se van ejecutando sucesivamente, siguiendo el orden de aparicin de stas. No hay saltos. Una condicin (condition) es una expresin lgica. Una estructura condicional (conditional structure) es una estructura que permite la ejecucin de una (o ms) sentencia(s) dependiendo de la evaluacin de una condicin. Tambin se les denomina sentencia condicional (conditional statement) ya que todo el bloque de la estructura forma una sentencia. Existen tres tipos: Simple, Doble y Mltiple.
92
II.1.1.
<condicin> es una expresin lgica <bloque if> es el bloque que se ejecutar si la expresin lgica se evala a true. Si hay varias sentencias dentro, es necesario encerrarlas entre llaves. Si slo hay una sentencia, pueden omitirse las llaves. Los parntesis que encierran la condicin son obligatorios. Ejemplo. Leer la edad de una persona e imprimir "Es mayor de edad" en el caso de que su edad sea mayor o igual que 18.
int edad; cin >> edad; if (edad >= 18){ cout << "\nEs mayor de edad"; } cout << "\nFn del programa";
o si se preere:
if (edad >= 18) cout << "\nEs mayor de edad"; cout << "\nFn del programa";
93
int edad; bool es_mayor_de_edad; cin >> edad; es_mayor_de_edad = (edad >= 18); if (es_mayor_edad == true) cout << "\nEs mayor de edad"; cout << "\nFn del programa";
Observad que la estructura condicional equivale a la siguiente:
94 $
Consejo:
&
No usad comparaciones del if (expresin_lgica == true) en las tructuras condicionales. Sustituirlas if (expresin_lgica)
tipo espor
%
95
#include <iostream> #include <cmath> using namespace std; int main(){ double a, b, c; // Parmetros de la ecuacin double raiz1, raiz2; // Races obtenidas double radical, denominador; 1 2 3 4 5 6 7 8 9 10 cout << "\nIntroduce coeficiente de 2o grado: "; cin >> a; cout << "\nIntroduce coeficiente de 1er grado: "; cin >> b; cout << "\nIntroduce coeficiente independiente: "; cin >> c; denominador = 2*a; radical = sqrt( b*b-4*a*c ); raiz1 = (-b + radical) / denominador; raiz2 = (-b - radical) / denominador;
11 cout << "\nLas races son" << raiz1 << " y " << raiz2; }
Flujo de control: (1,2,3,4,5,6,7,8,9,10,11).
96
Es a distinto de cero? Si. Entonces sigue la ejecucin. No. Entonces salta las sentencias 711 y termina.
#include <iostream> #include <cmath> using namespace std; int main(){ double a, b, c; // Parmetros de la ecuacin double raiz1, raiz2; // Races obtenidas double radical, denominador; cout << "\nIntroduce coeficiente de 2o grado: "; cin >> a; cout << "\nIntroduce coeficiente de 1er grado: "; cin >> b; cout << "\nIntroduce coeficiente independiente: "; cin >> c; if (a!=0) { denominador = 2*a; radical = sqrt( b*b-4*a*c ); raiz1 = (-b + radical) / denominador; raiz2 = (-b - radical) / denominador; cout << "\nLas races son" << raiz1 << " y " << raiz2; } }
97
El compilador se salta todos los separadores (espacios, tabulaciones, etc) entre las sentencias delimitadas por ;. Pero para favorecer la lectura del cdigo y enfatizar el bloque de sentencias incluidas en la estructura condicional, usaremos el siguiente estilo de codicacin:
cin >> c;
if (a!=0){ denominador = 2*a; radical = sqrt( b*b-4*a*c ); raiz1 = (-b + radical) denominador; raiz2 = (-b - radical) denominador; (
co!t "" #$n%as ra&ces son# "" raiz1 "" # ' # "" raiz2;
Lnea en blanco despus del del condicional Bloque if tabulado 3 espacios Llave cerrada (sin tabulacin)
Destacad visualmente el bloque de instrucciones de una estructura condicional. No seguir estas normas baja puntos en el examen.
98
Una notacin para describir algoritmos: Diagramas de ujo (owchart) Smbolos bsicos
INICIO Simbolo terminal Estructura secuencial
false
Bifurcador de flujo
99
cin >> a
cin >> b
cin >> c
false
a != 0
true
denominador = 2*a
(in
100
Ejemplo. Comprobad si un contribuyente es menor de edad o es mayor de 65, pero en cualquier caso con unos ingresos menores a 40000
int edad, ingresos; edad = 20; ingresos = 23000; if (edad < 18 || edad >= 65 && ingresos < 40000) // :-( cout << "\nContribuyente de especial tratamiento";
Cuidado con la precedencia de operadores. Debemos poner:
if ( (edad < 18 || edad >= 65) && ingresos < 40000) // :-) cout << "\nContribuyente de especial tratamiento";
101
Si hay varias sentencias, es necesario encerrarlas entre llaves. Dicho de otra forma: si no ponemos llaves, el compilador entiende que la nica sentencia del bloque if es la que hay justo debajo.
if (a!=0) denominador = 2*a; radical = sqrt( b*b-4*a*c ); raiz1 = (-b + radical) / denominador; raiz2 = (-b - radical) / denominador; cout << "\nLas races son" << raiz1 << " y " << raiz2;
Para el compilador es como si fuese:
if (a!=0){ denominador = 2*a; } radical = sqrt( b*b-4*a*c ); raiz1 = (-b + radical) / denominador; raiz2 = (-b - radical) / denominador; cout << "\nLas races son" << raiz1 << " y " << raiz2;
102
Ejemplo.
if (edad >= 18) cout << "\nEs mayor de edad"; cout << "\nFn del programa";
Y si la edad es menor de 18?
if (edad >= 18) cout << "\nEs mayor de edad"; if (edad < 18) cout << "\nEs menor de edad"; cout << "\nFn del programa";
La solucin propuesta funciona pero, aunque no lo parezca, se repite cdigo ya que se evalan condiciones mutuamente excluyentes: si la expresin edad >= 18 es true, la expresin edad < 18 siempre es false y viceversa. De hecho:
edad < 18
es equivalente a
if (edad >= 18) cout << "\nEs mayor de edad"; if (!(edad >= 18)) cout << "\nEs menor de edad";
Ahora se observa mejor que estamos repitiendo cdigo, violando por tanto el principio de una nica vez. Lo resolvemos usando un condicional doble.
103
II.1.2.
Condicional doble
if <condicin>
<bloque if>;
else
<bloque else>;
false <condicin>
true
bloque else
bloque if
int edad; cin >> edad; if (edad >= 18) cout << "\nEs mayor de edad"; else cout << "\nEs menor de edad"; cout << "\nFn del programa";
104
Qu pasa si a = 0 en la ecuacin de segundo grado? El algoritmo debe devolver c/b. Podemos hacer lo siguiente:
if (a!=0){ denominador = 2*a; radical = sqrt( b*b-4*a*c ); raiz1 = (-b + radical) / denominador; raiz2 = (-b - radical) / denominador; cout << "\nLas races son" << raiz1 << " y " << raiz2; } if (a==0) cout << "\nTiene una nica raz" << -c/b;
Pero estamos usando dos condiciones mutuamente excluyentes: (a!=0) y (a==0). Lo resolvemos usando un condicional doble.
105
#include <iostream> #include <cmath> using namespace std; int main(){ double a, b, c; // Parmetros de la ecuacin double raiz1, raiz2; // Races obtenidas double radical, denominador; cout << "\nIntroduce coeficiente de 2o grado: "; cin >> a; cout << "\nIntroduce coeficiente de 1er grado: "; cin >> b; cout << "\nIntroduce coeficiente independiente: "; cin >> c; if (a!=0) { denominador = 2*a; radical = sqrt( b*b-4*a*c ); raiz1 = (-b + radical) / denominador; raiz2 = (-b - radical) / denominador; cout << "\nLas races son" << raiz1 << " y " << raiz2; } else{ raiz1 = -c/b; cout << "\nLa nica raz es " << raiz1; } }
106
Inicio
cin >> a
cin >> b
cin >> c
false
a != 0
true
denominador = 2*a
(in
107
cin >> a; cin >> b; if (a >= b) max = a; else max = b; cout << "\nEl mayor es " << max;
Ejemplo. El mayor de tres nmeros a, b y c (primera aproximacin). Algoritmo: Entradas: a, b y c Salidas: El mayor entre a, b y c Descripcin:
Si a es mayor que los otros, el mayor es a Si b es mayor que los otros, el mayor es b Si c es mayor que los otros, el mayor es c
Implementacin:
if ((a>=b) && (a>=c)) max = a; if ((b>=a) && (b>=c)) max = b; if ((c>=a) && (c>=b)) max = c;
108
(a>=b) y (a>=c)
false
true
max = a
(b>=a) y (b>=c)
false
true
max = b
(c>=a) y (c>=b)
false
true
max = c
109
II.1.3.
Dentro de un bloque if (else), puede incluirse otra estructura condicional, anidndose tanto como permita el compilador. Cundo se ejecuta cada instruccin?
if (condic_1) { inst_1; if (condic_2) { inst_2; } else { inst_3; } inst_4; } else { inst_5; } inst_1 inst_2 inst_3 inst_4 inst_5
condic_1
true true true true false
condic_2
independiente true false independiente independiente
110
Ejemplo. El mayor de tres nmeros (segunda aproximacin) Algoritmo: Entradas y Salidas: idem Descripcin:
Si a es mayor que b, entonces Calcular el mximo entre a y c En otro caso, Calcular el mximo entre b y c
Implementacin:
if (a>=b) if (a>=c) max = a; else max = c; else if (b>=c) max = b; else max = c;
111
true
a>=b
false
true
a>=c
false
true
b>=c
false
max = a
max = c
max = b
max = c
112
Ejemplo. El mayor de tres nmeros (tercera aproximacin) Algoritmo: Entradas y Salidas: idem Descripcin e Implementacin:
/* Calcular el mximo (max) entre a y b. Calcular el mximo entre max y c. */ if (a>=b) max = a; else max = b; if (c>max) max = c;
113
false
a>=b
true
max = b
max = a
c>max false
true
max = c
114
Con qu algoritmo nos quedamos? Supongamos que todos los valores son distintos La solucin 1 siempre evala 3 condiciones y realiza 1 asignacin. La solucin 2 siempre evala 2 condiciones y realiza 1 asignacin. La solucin 3 siempre evala 2 condiciones y realiza 1 2 asignaciones. La solucin 2 realiza alguna evaluacin menos, pero la solucin 3 es muchsimo mejor: Es mucho ms fcil de entender No repite cdigo Es mucho ms fcil de extender a varios valores.
115
if (a>=b) if (a>=c) if (a>=d) max = a; else max = d; else if (c>=d) max = c; else max = d; else if (b>=c) if (b>=d) max = b; else max = d; else if (c>=d) max = c; else max = d;
116
El mayor de cuatro nmeros con la tercera aproximacin: Algoritmo: Entradas y Salidas: idem Descripcin e Implementacin:
/* Calcular el mximo (max) entre a y b. Calcular el mximo entre max y c. Calcular el mximo entre max y d. */ if (a>=b) max = a; else max = b; if (c>max) max = c; if (d>max) max = d;
En general:
Calcular el mximo (max) entre a y b. Para cada uno de los valores restantes, calcular el mximo entre max y dicho valor.
117
// Programa que permite operar con dos nmeros enteros #include <iostream> using namespace std; int main(){ int dato1, dato2, resultado; char opcion; cout << "\nIntroduce el primer operando: "; cin >> dato1; cout << "\nIntroduce el segundo operando: "; cin >> dato2; cout << "\nElija (S)Sumar, (R)Restar, (M)Multiplicar: "; cin >> opcion; if (opcion cout << if (opcion cout << if (opcion cout << if (opcion cout << } == 'S') "\nSuma = " << dato1+dato2; == 'R') "\nResta = " << dato1-dato2; == 'M') "\nMultiplicacin = " << dato1*dato2; != 'R' && opcion != 'S' && opcion != 'M') "\nNinguna operacin";
118
if (opcion == 'S') cout << "\nSuma = " << dato1+dato2; else if (opcion == 'R') cout << "\nResta = " << dato1-dato2; else if (opcion == 'M') cout << "\nMultiplicacin = " << dato1*dato2; else cout << "\nNinguna operacin";
if (opcion == 'S') cout << "\nSuma = " << dato1+dato2; else if (opcion == 'R') cout << "\nResta = " << dato1-dato2; else if (opcion == 'M') cout << "\nMultiplicacin = " << dato1*dato2; else cout << "\nNinguna operacin"; }
119
Cmo describimos un algoritmo? Descripcin de un algoritmo: Se trata de describir la idea principal del algoritmo, de forma concisa y esquemtica, sin entrar en detalles innecesarios.
120
/* Si a es >= b, asignamos a max el valor a En otro caso, le asignamos b. Una vez hecho lo anterior, vemos si c es mayor que max, en cuyo caso le asignamos c */ if (a>=b) max = a; else max = b; if (c>max) max = c;
Seremos esquemticos (pocas palabras en cada lnea)
/* Calcular el mximo entre a y b, y una vez hecho esto, pasamos a calcular el mximo entre el anterior, al que llamaremos max, y el nuevo valor c */ /* Calcular el mximo (max) entre a y b. Calcular el mximo entre max y c. */
121
Comentaremos un bloque completo La descripcin del algoritmo la incluiremos antes de un bloque, pero nunca entre las lneas del cdigo. Esto nos permite separar las dos partes y poder leerlas independientemente.
// Calcular el mximo entre a y b if (a>=b) max = a; else // En otro caso: max = b; // Calcular el mximo entre max y c if (c>max) max = c;
Algunos autores incluyen la descripcin a la derecha del cdigo, pero es mucho ms difcil de mantener (si incluimos lneas de cdigo nuevas, se descompone todo el comentario):
// // // //
La descripcin del comentario es correcta pero no el sitio (a la derecha de cada lnea) Calcular el mximo entre a y c Calcular el mximo entre max y c
Usad descripciones de algoritmos que sean concisas con una presentacin visual agradable y esquemtica. Las descripciones han de ser sobre un bloque completo. Slo se usarn comentarios al nal de una lnea en casos puntuales, para aclarar el cdigo de dicha lnea. No seguir estas normas baja puntos en el examen.
122
II.1.4.
II.1.4.1.
Cualquier programa medianamente complejo contiene miles de lneas de cdigo escritas por varios equipos de programadores especialistas en facetas distintas. En un tema posterior veremos que los bloques de cdigo se agruparn en funciones, clases, bibliotecas, etc. Como mnimo habr un equipo especialista en la interfaz de usuario (entradas y salidas de datos E/S). Para poder aislar los bloques de cdigo correspondientes a las E/S, impondremos que si dentro de un bloque realizamos alguna operacin de E/S, en dicho bloque no realizaremos ningn cmputo propio del programa.
Interfaz de usuario consulta Cmputos
resultados
En nuestros programas la interfaz de usuario es muy sencilla y se basa en las rdenes cin y cout. An as, como buenos programadores, separaremos los bloques de E/S de los cmputos. Para ello, usaremos variables que indiquen lo que ha ocurrido en un bloque.
123
Ventajas de la separacin de E/S y cmputos: Se separan responsabilidades. Cada equipo de desarrollo puede actualizar su parte, sin afectar al resto. Se favorece la reutilizacin entre plataformas (Linux, Windows, etc). Si en un bloque usamos cout por ejemplo, dicho bloque slo funcionar en una ventana de consola y no en una ventana tpica de Windows.
int edad; cin >> edad; if (edad >= 18) // <- Cmputo cout << "\nEs mayor de edad"; // <- E/S
Para separar E/S es_mayor_edad: de cmputos introducimos la variable
int edad; bool es_mayor_edad; // Entrada de datos: cin >> edad; // Cmputos: es_mayor_edad = (edad>18); // Salida de resultados: if (es_mayor_edad) cout << "\nEs mayor de edad";
Esto me permite reutilizar el cdigo es_mayor_edad = (edad>18); en otros sitios:
124
Los bloques que realizan entradas o salidas de datos (cin, cout) estarn separados de los bloques que realizan cmputos.
Ampliacin:
Los bloques de cdigo de entradas de datos, salida de resultados, cmputos, etc se aslan en lo que se denominan capas (layers) . Adems, cada una de estas capas puede ejecutarse en distintos sitios fsicos o niveles (tiers) (como en el ejemplo de la bsqueda en la web)
125
if (opcion == 'S') cout << "\nSuma = " << dato1+dato2; else if (opcion == 'R') cout << "\nResta = " << dato1-dato2; else if (opcion == 'M') cout << "\nMultiplicacin = " << dato1*dato2; else cout << "\nNinguna operacin";
En un bloque usamos variables que almacenen resultados y en otro bloque las usamos o mostramos:
bool operacion_valida; int dato1, dato2, resultado; // Bloque de entrada de datos ........... // Bloque de cmputos operacion_valida = true; if (opcion == 'S') resultado = dato1+dato2; else if (opcion == 'R') resultado = dato1-dato2; else if (opcion == 'M') resultado = dato1*dato2; else operacion_valida = false;
126
// Bloque de salida de resultados if (operacion_valida){ if (opcion == 'S') cout << "\nSuma = "; else if (opcion == 'R') cout << "\nResta = "; else if (opcion == 'M') cout << "\nMultiplicacin = "; cout << resultado; } else cout << "\nNinguna operacin";
Somos conscientes que volvemos a evaluar las condiciones sobre la opcin, pero el benecio de separar E/S y cmputos es mayor. Adems, con un esfuerzo adicional, tambin podemos evitar la evaluacin doble de condiciones. El programa quedara nalmente as:
bool operacion_valida; int dato1, dato2, resultado; char opcion; string mensaje_a_imprimir; // Bloque de entrada de datos cout << "\nIntroduce el primer operando: "; cin >> dato1; cout << "\nIntroduce el segundo operando: "; cin >> dato2; cout << "\nSelecciona (S) sumar, (R) restar, (M) multiplicar: "; cin >> opcion;
127
// Bloque de cmputos operacion_valida = true; if (opcion == 'S'){ mensaje_a_imprimir = "\nSuma = "; resultado = dato1+dato2; } else if (opcion == 'R'){ mensaje_a_imprimir = "\nResta = "; resultado = dato1-dato2; } else if (opcion == 'M'){ mensaje_a_imprimir = "\nMultiplicacin = "; resultado = dato1*dato2; } else{ mensaje_a_imprimir = "\nNinguna operacin"; operacion_valida = false; } // Bloque de salida de resultados cout << mensaje_a_imprimir; if (operacion_valida) cout << resultado;
Ejercicio. Utilizad una variable lgica contribuyente_especial y reescribid el cdigo siguiente, separando E/S y cmputos:
if ( (edad<18 || edad>=65) && ingresos<40000) // :-) cout << "\nContribuyente de especial tratamiento";
128
II.1.4.2.
Anidar o no anidar?
Un ejemplo en el que es mejor anidar Ejemplo. Reescribid el siguiente ejemplo utilizando nicamente estructuras condicionales no anidadas:
if (A>B) if (B<=C) if (C!=D) <S1>; else <S2>; else <S3>; else <S4>;
if ((A>B) && (B<=C) && (C!=D)) <S1>; if ((A>B) && (B<=C) && (C==D)) <S2>; if ((A>B) && (B>C)) <S3>; if (A<=B) <S4>;
Preferimos el primero ya que realiza menos comprobaciones, y sobre todo porque no duplica cdigo (el de las condiciones) y es, por tanto, menos propenso a errores de escritura o ante posibles cambios futuros.
129
if (c1) if (c2) bloque_A else bloque_B else bloque_B bloque_A: bloque_B: c1 true c2 true (c1&&c2) false: c1 true y c2 false c1 false y c2 true c1 false y c2 false c1 true c2 true c1 true y c2 false o bien c1 false
Primer caso:
130
Son equivalentes. Elegimos la primera porque: Al aumentar el anidamiento se va perdiendo en legibilidad y concisin. Muy importante: la segunda opcin repite cdigo (bloque_B) y por tanto es ms propenso a errores de escritura o ante posibles cambios futuros.
' $
Los factores a tener en cuenta (entre otros) para anidar o no estructuras condicionales son: Que no se repita cdigo.
&
131
II.1.4.3.
Debemos intentar simplicar las expresiones lgicas, para que sean fciles de entender. Usaremos las leyes del lgebra de Boole, muy similares a las conocidas en Matemticas elementales como: (a + b) = a b a(b + c) = ab + ac
a a a a
!A !A (A (A
&& || && ||
!B !B B) || (A && C) B) && (A || C)
Nota. Las dos primeras (relativas a la negacin) se conocen como Leyes de Morgan. Las dos siguientes son la ley distributiva (de la conjuncin con respecto a la disyuncin y viceversa). Ampliacin:
Consultad:
http://es.wikipedia.org/wiki/Algebra_booleana http://serbal.pntic.mec.es/~cmunoz11/boole.pdf
132
Ejemplo. Es trabajador activo si su edad no est por debajo de 18 o por encima de 65.
bool es_activo; int edad; cin >> edad; es_activo = !(edad<18 || edad>65); if (es_activo) cout << "\nTrabajador en activo"; ......
!(edad<18 || edad>65) equivale a !(edad<18) && !(edad>65) equivale a (edad >= 18) && (edad <= 65)
Es ms fcil de entender el siguiente cdigo:
es_activo = (edad >= 18) && (edad <= 65); if (es_activo) cout << "\nTrabajador en activo";
133
Ejemplo. Es posible beneciario de ayudas si no es un trabajador activo. Adems, ya sea menor de 18 aos o mayor de 65, la unidad familiar a la que pertenezca no puede tener una base imponible mayor de 35000 euros.
bool beneficiario; ....... beneficiario = ((base_imponible <= 35000) && (edad < 18)) || ((base_imponible <= 35000) && (edad > 65));
Es mucho ms fcil de entender lo siguiente (aplicando la lay distributiva):
134
II.1.4.4.
La expresin 1.0e15 == (1.0/3.0)*3.0 podra evaluarse a false debido a la precisin nita para calcular (1.0/3.0) (0.333333333) Es ms:
double descuento_base, porcentaje; descuento_base = 0.1; porcentaje = descuento_base * 100; if (porcentaje == 10) // Podra evaluarse a false :-O cout << "Descuento del 10%"; ....
Recordad que la representacin en coma otante no es precisa y 0.1 ser internamente un valor prximo a 1, pero no exacto. Soluciones: Fomentar condiciones de desigualdad Mejor: Fijar un error de precisin y aceptar igualdad cuando la diferencia de las cantidades sea menor que dicho error.
double real1, real2; const double epsilon = 0.00001; if ((real1-real2) < epsilon) cout << "Son iguales";
135
Ampliacin:
Como no es lo mismo comparar si 0.000001 est cerca de 0.000002, que comparar si 130.001 est cerca de 130.002, el mtodo anterior hay que renarlo. Para ms informacin:
136
II.1.4.5.
Evaluacin en ciclo corto (Short-circuit evaluation) : El compilador optimiza la evaluacin de expresiones lgicas evaluando sus trminos de izquierda a derecha hasta que sabe el resultado de la expresin completa (lo que signica que puede dejar trminos sin evaluar). La mayora de los compiladores realizan este tipo de evaluacin por defecto. Evaluacin en ciclo largo (Eager evaluation) : El compilador evala todos los trminos de la expresin lgica para conocer el resultado de la expresin completa. Ejemplo.
137
II.1.5.
Recordemos el ejemplo de lectura de dos enteros y una opcin de la pgina 126. Nos centramos en el bloque de cmputos:
operacion_valida = true; if (opcion == 'S'){ mensaje_a_imprimir = "\nSuma = "; resultado = dato1+dato2; } else if (opcion == 'R'){ mensaje_a_imprimir = "\nResta = "; resultado = dato1-dato2; } else if (opcion == 'M'){ mensaje_a_imprimir = "\nMultiplicacin = "; resultado = dato1*dato2; } else{ mensaje_a_imprimir = "\nNinguna operacin"; operacion_valida = false; }
Podemos reescribir este cdigo utilizando otra estructura alternativa.
138
break;
......................... [default: <sentencias>] }
139
operacion_valida = true; switch (opcion) { case 'S': mensaje_a_imprimir = "\nSuma = "; resultado = dato1+dato2; break; case 'R': mensaje_a_imprimir = "\nResta = "; resultado = dato1-dato2; break; case 'M': mensaje_a_imprimir = "\nMultiplicacin = "; resultado = dato1*dato2; break; default: mensaje_a_imprimir = "\nNinguna operacin"; operacion_valida = false; }
140
El gran problema con la estructura switch es que el programador olvidar en ms de una ocasin, incluir la sentencia break. La nica ventaja es que se pueden realizar las mismas operaciones para un grupo determinado de constantes:
operacion_valida = true; switch (opcion) { case 's': case 'S': mensaje_a_imprimir = "\nSuma = "; resultado = dato1+dato2; break; case 'r': case 'R': mensaje_a_imprimir = "\nResta = "; resultado = dato1-dato2; break; case 'm': case 'M': mensaje_a_imprimir = "\nMultiplicacin = "; resultado = dato1*dato2; break; default: mensaje_a_imprimir = "\nNinguna operacin"; operacion_valida = false; }
141
Pero para conseguir ese objetivo, la siguiente solucin sera mucho mejor:
cin >> opcion; opcion = toupper(opcion); operacion_valida = true; switch (opcion) { case 's': case 'S': mensaje_a_imprimir = "\nSuma = "; resultado = dato1+dato2; break; case 'r': case 'R': mensaje_a_imprimir = "\nResta = "; resultado = dato1-dato2; break; case 'm': case 'M': mensaje_a_imprimir = "\nMultiplicacin = "; resultado = dato1*dato2; break; default: mensaje_a_imprimir = "\nNinguna operacin"; operacion_valida = false; }
Y para no tener errores lgicos si se nos olvida incluir break, podemos volver a lo que ya sabemos:
142
mensaje_a_imprimir = "\nSuma = "; resultado = dato1+dato2; } else if (opcion == 'R'){ mensaje_a_imprimir = "\nResta = "; resultado = dato1-dato2; } else if (opcion == 'M'){ mensaje_a_imprimir = "\nMultiplicacin = "; resultado = dato1*dato2; } else{ mensaje_a_imprimir = "\nNinguna operacin"; operacion_valida = false; }
' $
Consejo:
&
Slo para C++ En la medida de lo posible, evitad el uso de la estructura switch por los errores lgicos producidos al olvidarnos incluir algn break
143
II.2.
Estructuras repetitivas
Una estructura repetitiva (iteration/loop) (tambin conocidas como bucles, ciclos o lazos) permite la ejecucin de una secuencia de sentencias: o bien, hasta que se satisface una determinada condicin Bucle controlado por condicin (Condition-controlled loop) o bien, un nmero determinado de veces Bucle controlado por contador (Counter controlled loop)
II.2.1.
II.2.1.1.
Pre-test
Post-test
while (<condicin>) {
<cuerpo bucle> }
do {
<cuerpo bucle> } while (<condicin>);
Funcionamiento: En ambos, se va ejecutando el cuerpo del bucle mientras la condicin sea verdad. En un bucle pre-test (pre-test loop) (while) se evala la condicin antes de entrar al bucle y luego (en su caso) se ejecuta el cuerpo. En un bucle post-test (post-test loop) (do while) primero se ejecuta el cuerpo y luego se evala la condicin.
Estructuras de Control Estructuras repetitivas Bucles controlados por condicin: pre-test y post-test
144
Cada vez que se ejecuta el cuerpo del bucle diremos que se ha producido una iteracin (iteration)
(Pretest)
(Posttest)
false
<condicion>
true
false
Estructuras de Control Estructuras repetitivas Bucles controlados por condicin: pre-test y post-test
145
II.2.1.2.
Ejemplo. Crear un ltro (lter) de entrada de datos: Leer un valor y no permitir al usuario que lo introduzca fuera de un rango determinado.
// Introducir un nico valor, pero positivo. // Imprimir el coseno. #include <iostream> #include <cmath> using namespace std; int main(){ double valor; do{ cout << "\nIntroduzca un valor positivo: "; cin >> valor; }while (valor < 0); cout << cos(valor); }
Nota. El ltro anterior no nos evita todos los posibles errores. Por ejemplo, si se introduce un valor demasiado grande, se produce un desbordamiento y el resultado almacenado en valor es indeterminado. Nota. Observad que el estilo de codicacin se rige por las mismas normas que las indicadas en la estructura condicional (pgina 97)
Estructuras de Control Estructuras repetitivas Bucles controlados por condicin: pre-test y post-test
146
num_lineas = 1; do{ cout << '\n' << "*****" ; num_lineas = num_lineas + 1; }while (num_lineas <= 20);
O bien:
// nuevo = antiguo + 1
num_lineas = 0; do{ cout << '\n' << "*****" ; num_lineas = num_lineas + 1; }while (num_lineas < 20);
Estructuras de Control Estructuras repetitivas Bucles controlados por condicin: pre-test y post-test
147
O bien:
num_lineas = 1; while (num_lineas <= 20){ cout << '\n' << "*****" ; num_lineas = num_lineas + 1; }
Preferible:
num_lineas = 0;
while (num_lineas < 20){ cout << '\n' << "*****" ; num_lineas = num_lineas + 1; }
Dentro del bucle, JUSTO antes de comprobar la condicin, la variable total contiene el nmero de lneas que hay impresas :-) Nota. Podramos usar el operador de incremento:
while (num_lineas < 20){ cout << '\n' << "*****" ; num_lineas++; }
Estructuras de Control Estructuras repetitivas Bucles controlados por condicin: pre-test y post-test
148
Eleccin entre un bucle pre-test o post-test: Hay situaciones en las que no queremos que se ejecute el cuerpo del bucle? S pre-test, No post-test Ejercicio. Leer un nmero positivo tope e imprimir tope lneas con 5 estrellas cada una.
cout << "\nCuntas lneas de asteriscos quiere imprimir? "; cin >> tope; num_lineas = 0; do{ cout << '\n' << "*****" ; num_lineas++; }while (num_lineas < tope);
Problema: Qu ocurre si tope = 0? Ejercicio. Resolved el problema anterior con un bucle pre-test
'
Consejo:
&
Fomentad el uso de los bucles pre-test. Casi siempre habr algn caso en el que no queramos ejecutar el cuerpo del bucle ni siquiera una vez.
Estructuras de Control Estructuras repetitivas Bucles controlados por condicin: pre-test y post-test
149
cin >> tope; par = 0; while (par <= tope){ par = par + 2; cout << par; }
Al nal, escribe uno ms. Cambiamos el orden de las instrucciones:
cin >> tope; par = 0; while (par <= tope){ cout << par; par = par + 2; }
// Primer candidato // Es bueno? // Si => imprmelo // calcular nuevo // candidato // No => Salir
par = 2
' $
Consejo:
&
En el diseo de los bucles siempre hay que comprobar el correcto funcionamiento en los casos extremos (primera y ltima iteracin)
Estructuras de Control Estructuras repetitivas Bucles controlados por condicin: pre-test y post-test
150
Ir dividiendo n por 10 hasta llegar a una cifra El nmero de dgitos ser el nmero de iteraciones num_digitos = 1; while (n > 9){ n = n/10; num_digitos++; }
Mejor si no modicamos la variable original:
Estructuras de Control Estructuras repetitivas Bucles controlados por condicin: pre-test y post-test
151
II.2.1.3.
Lectura Anticipada
Ejemplo. Realizad un programa que sume una serie de valores ledos desde teclado, hasta que se lea el valor -1 (terminador = -1)
#include<iostream> using namespace std; int main(){ int suma, numero; suma = 0; do{ cin >> numero; suma = suma + numero; }while (numero != -1); cout << "\nLa suma es " << suma; }
Problema: Procesa el -1 y lo suma.
Estructuras de Control Estructuras repetitivas Bucles controlados por condicin: pre-test y post-test
152
do{ cin >> numero; if (numero != -1) suma = suma + numero; }while (numero != -1);
Funciona, pero evala dos veces la misma condicin, lo cual es ineciente y, mucho peor, duplica cdigo. Recordad: Evitad el uso de bucles do while Solucin: tcnica de lectura anticipada. Leemos el primer valor antes de entrar al bucle y comprobamos si hay que procesarlo (el primer valor podra ser ya el terminador)
// Primer candidato
while (numero != -1) { // Es bueno? suma = suma + numero; // Lo procesamos cin >> numero; // Leer siguiente candidato } cout << "\nLa suma es " << suma; }
Nota. La primera vez que entra al bucle, la instruccin
Estructuras de Control Estructuras repetitivas Bucles controlados por condicin: pre-test y post-test
153
Ejercicio.
// Programa que va leyendo nmeros enteros hasta que se // lea el cero. Imprimir el nmero de pares e impares ledos. #include <iostream> using namespace std; int main(){ int ContPar, ContImpar, valor; ContPar=0; ContImpar=0; cout << "\nIntroduce valor: "; cin >> valor; while ( if ( else ; cout << "\nIntroduce valor: "; cin >> valor; } cout << "\nFueron " << ContPar << " pares y " << ContImpar << " impares"; } ) { ) ;
Estructuras de Control Estructuras repetitivas Bucles controlados por condicin: pre-test y post-test
154
II.2.1.4.
Condiciones compuestas
Es normal que necesitemos comprobar ms de una condicin en un bucle. Dependiendo del algoritmo necesitaremos conectarlas con && o con ||. Ejemplo. Leer una opcin de un men. Slo se admite s n.
char opcion; do{ cout << "Desea formatear el disco?"; cin >> opcion; }while ( opcion!='s' opcion!='S' opcion!='n' opcion!='N' );
Cundo quiero salir del bucle? Cuando cualquiera de las condiciones sea false. Cual es el operador que cumple lo siguiente?:
false
Operador
= false
Nota. En este ejemplo, mucho mejor si pasamos la opcin a mayscula dentro del bucle y comprobamos nicamente las condiciones con los caracteres en mayscula
Estructuras de Control Estructuras repetitivas Bucles controlados por condicin: pre-test y post-test
155
Ejemplo. Leer dos valores desde el teclado forzando a que ambos sean distintos de cero.
do{ cout << "\nIntroduzca un numero: "; cin >> dato1; cout << "\nIntroduzca otro numero: "; cin >> dato2; }while (dato1==0 dato2==0);
Cundo quiero salir del bucle? Cuando ambas condiciones, simultneamente , sean false. En cualquier otro caso, entro de nuevo al bucle. Cual es el operador que cumple lo siguiente?:
false true
Operador Operador
true
En cualquier caso, hubiese sido mucho mejor leer cada valor en un bucle independiente. De esa forma, si nos equivocamos al introducir uno pero no el otro, slo tendremos que volver a leer el valor incorrecto. Hacedlo en la casa.
' $
Consejo:
&
E n la construccin de condiciones compuestas, empezad planteando las condiciones simples que la forman. Pensad cuando queremos salir del bucle y conectad adecuadamente dichas condiciones simples, dependiendo de si nos queremos salir cuando todas simultneamente sean false (OR) o cuando cualquiera de ellas sea false (AND)
Estructuras de Control Estructuras repetitivas Bucles controlados por condicin: pre-test y post-test
156
Ejemplo. Calcular el mximo comn divisor de dos nmeros a y b. Algoritmo: Entradas: Los dos enteros a y b Salidas: el entero max_com_div, mximo comn divisor de a y b Descripcin e Implementacin:
/* Empezar como primer posible divisor con el menor de ambos nmeros Mientras divisor no divida a ambos, probar con el divisor anterior */ if (b<a) menor = b; else menor = a; divisor = menor; while ((a % divisor != 0) divisor --; max_com_div = divisor; (b % divisor != 0))
Estructuras de Control Estructuras repetitivas Bucles controlados por condicin: pre-test y post-test
157
El uso de una variable lgica har que el algoritmo sea ms fcil de entender:
mcd_encontrado = false; while (!mcd_encontrado){ if ((a%divisor == 0) (b%divisor == 0)) mcd_encontrado = true; else divisor--; } max_com_div = divisor;
Qu pasara si quitsemos el else?
Estructuras de Control Estructuras repetitivas Bucles controlados por condicin: pre-test y post-test
158
Recorrer los impares menores que el nmero hasta encontrar un divisor o hasta llegar a dicho nmero int impar; cin >> n; impar = 3; while (n % impar != 0 && impar = impar + 2; n > impar)
Una vez terminado el bucle, cmo comprobamos cual fue la condicin que nos hizo salir?
if (n % impar == 0) cout << "\nPrimer divisor impar: " << impar; else cout << "\n" << n << " no tiene divisores impares propios";
Y si el bucle termin por la otra condicin (n es un primo igual a impar)? En este caso n % impar sera tambin cero, pero no queremos imprimir el mismo n como un divisor suyo.
if (impar < n && n % impar == 0) cout << "\nPrimer divisor impar: " << impar; else cout << "\n" << n << " no tiene divisores impares propios";
Estructuras de Control Estructuras repetitivas Bucles controlados por condicin: pre-test y post-test
159
bool hay_posibles_divisores, divisor_encontrado; hay_posibles_divisores = true; divisor_encontrado = false; impar = 3; while (!divisor_encontrado && hay_posibles_divisores){ if (impar >= n) hay_posibles_divisores = false; else if (n % impar == 0) // Importante el orden divisor_encontrado = true; // al evaluar las condiciones else impar = impar+2; } if (divisor_encontrado) cout << "\nPrimer divisor impar: " << impar; else cout << "\n" << n << " no tiene divisores impares propios";
Nota. Podramos salir del bucle en n/2
' $
Consejo:
&
Fomentad el uso de variables lgicas para controlar condiciones complejas de salida de los bucles. Usaremos tantas variables lgicas como condiciones necesitemos controlar, y las conectaremos apropiadamente con los operadores lgicos.
Estructuras de Control Estructuras repetitivas Bucles controlados por condicin: pre-test y post-test
160
Ejemplo. Leer nmeros enteros de la entrada por defecto hasta que se introduzcan 10 hasta que se introduzca un nmero negativo. Imprimir la media aritmtica.
suma = 0; cin >> valor; total_introducidos = 1; while ((valor >= 0) && (total_introducidos <= 10)){ suma = suma + valor; total_introducidos++; cin >> valor; } media = suma/total_introducidos;
Problema: Lee el undcimo y se sale, pero ha tenido que leer dicho valor. Solucin: O bien cambiamos la inicializacin de total_introducidos a 0, o bien leemos hasta 9. En cualquier caso, el dcimo valor hay que procesarlo fuera del bucle.
....... while ((valor >= 0) && (total_introducidos < 10)){ suma = suma + valor; total_introducidos++; cin >> valor; } suma = suma + valor; media = suma/total_introducidos;
Estructuras de Control Estructuras repetitivas Bucles controlados por condicin: pre-test y post-test
161
Problema: Si se ha salido con un negativo lo suma. Adems, total_introducidos tambin se incrementa en uno con el valor negativo, por lo que habra que tenerlo en cuenta a la hora de hallar la media aritmtica. Quedara:
suma = 0; cin >> valor; total_introducidos = 1; while ((valor >= 0) && (total_introducidos<10)){ suma = suma + valor; total_introducidos++; cin >> valor; } if (total_introducidos == 10) suma = suma + valor; else total_introducidos--; media = suma/total_introducidos;
Pero y si el ltimo ledo es negativo?
if ((total_introducidos == 10) && (valor >=0)) suma = suma + valor; else total_introducidos--;
Podemos observar la complejidad (por no decir chapucera) innecesaria que ha alcanzado el programa.
Estructuras de Control Estructuras repetitivas Bucles controlados por condicin: pre-test y post-test
162
suma = 0; num_positivos = 0; es_positivo = true; llevo_menos_de_10 = true; while (es_positivo && llevo_menos_de_10){ cin >> valor; if (valor < 0) es_positivo = false; else{ suma = suma + valor; num_positivos++; if (num_positivos == 10) llevo_menos_de_10 = false; } } media = suma/(1.0*num_positivos); // Si num_positivos es 0 // media = infinito (correcto) if (num_positivos == 0) cout << "\nNo se introdujeron valores"; else cout << "\nMedia aritmtica = " << media;
Consejo:
Construid los bucles de forma que no haya que arreglar nada despus de su nalizacin
Estructuras de Control Estructuras repetitivas Bucles controlados por condicin: pre-test y post-test
163
Bajemos puntos en el examen. El algoritmo es el mismo, pero observad los nombres de variables:
aux = 0; contador = 0; seguir_1 = true; seguir_2 = true; while (seguir_1 && seguir_2){ cin >> v; if (v < 0) seguir_1 = false; else{ aux = aux + v; contador++; if (contador == 10) seguir_2 = false; } } resultado = aux/(1.0*contador); if (contador == 0) cout << "\nNo se introdujeron valores"; else cout << "\nMedia aritmtica = " << resultado;
Estructuras de Control Estructuras repetitivas Bucles controlados por condicin: pre-test y post-test
164
Algunas citas sobre la importancia de escribir cdigo que sea fcil de entender:
"Any fool can write code that a computer can understand. Good programmers write code that humans can understand". Martin Fowler "Programs must be written for people to read, and only incidentally for machines to execute". Abelson & Sussman "Always code as if the guy who ends up maintaining your code will be a violent psychopath who knows where you live".
Principio de Programacin: Sencillez (Simplicity) Fomentad siempre la sencillez y la legibilidad en la escritura de cdigo
"There are two ways of constructing a software design: One way is to make it so simple that there are obviously no deciencies, and the other way is to make it so complicated that there are no obvious deciencies. The rst method is far more difcult.". C.A.R. Hoare
Estructuras de Control Estructuras repetitivas Bucles controlados por condicin: pre-test y post-test
165
#include <iostream> #include <cmath> using namespace std; int main(){ int valor, divisor; bool es_primo; cout << "Introduzca un numero natural: "; cin >> valor; es_primo = true; divisor = 2 while (divisor < valor){ if (valor % divisor == 0) es_primo = false; divisor++; } if (es_primo) cout << valor << " es primo\n"; else cout << valor << " no es primo\n"; }
Para hacerlo ms eciente, nos salimos en cuanto sepamos que no es primo. Nos salimos del bucle con un bool. La misma variable es_primo nos sirve:
Estructuras de Control Estructuras repetitivas Bucles controlados por condicin: pre-test y post-test
166
es_primo = true; divisor = 2 while (divisor < valor && es_primo){ if (valor % divisor == 0) es_primo = false; else divisor++; }
Incluso podramos quedarnos en sqrt(valor), ya que si valor no es primo, tiene al menos un divisor menor que sqrt(valor). En cualquier caso, sqrt es una operacin costosa y habra que evaluarlo empricamente.
es_primo = true; tope = sqrt(valor); divisor = 2 while (divisor < tope && es_primo){ if (valor % divisor == 0) es_primo = false; else divisor++; }
Nota. Realmente, para que compile sin problemas debemos poner tope = sqrt(1.0*valor);
Estructuras de Control Estructuras repetitivas Bucles controlados por condicin: pre-test y post-test
167
Ampliacin:
Cuando valor es un entero muy grande, el algoritmo anterior no termina en un tiempo razonable. Qu es un nmero grande?
http://www.naturalnumbers.org/bignum.html
Qu signica tiempo razonable? Est fuera del alcance del curso e implica nociones ms profundas sobre complejidad computacional Para una introduccin informal pero clara:
http://www.claymath.org/Popular_Lectures/Minesweeper/
El problema terico ms importante en Informtica es determinar si existen problemas que no pueden resolverse en un tiempo razonable con un algoritmo (aunque s exista un algoritmo que compruebe rpidamente si una propuesta es una solucin o no). Este problema est an sin resolver y es uno de los siete problemas del milenio, recompensado con 1 milln de dlares por el Instituto Clay de Matemticas. En Agosto de 2010 un cientco de HP proclam haberlo demostrado, pero en pocas semanas se encontraron fallos en la demostracin. El primer algoritmo para determinar en un tiempo razonable si un nmero es primo, es bastante reciente (2002):
http://primes.utm.edu/
Estructuras de Control Estructuras repetitivas Bucles controlados por condicin: pre-test y post-test
168
II.2.1.5.
Bucles sin n
contador = 2; while (contador < 3) { contador--; cout << '\n' << contador; }
169
II.2.2.
II.2.2.1.
Se utilizan para repetir un conjunto de sentencias un nmero de veces jado de antemano. Se necesita una variable contadora, un valor inicial, un valor nal y un incremento. Ejemplo. Hallar la media aritmtica de cinco enteros ledos desde teclado.
int main(){ int contador, valor, suma, inicio, final; double media; inicio = 1; final = 5; suma = 0; contador = inicio; while (contador <= final){ cout << "\nIntroduce un nmero "; cin >> valor; suma = suma + valor; contador = contador + 1; } media = suma / (final *1.0); cout << "\nLa media es " << media; }
170
contador = inicio
false
true
contador = contador + 1
contador es la variable controladora del ciclo. inicio es el valor inicial que toma la variable controladora. final es el valor nal que toma la variable controladora.
171
II.2.2.2.
Formato
La sentencia for permite la construccin de una forma compacta de los ciclos controlados por contador, aumentando la legibilidad del cdigo.
int main(){ int contador, valor, suma, inicio, final; double media; inicio = 1; final = 5; suma = 0; for (contador = inicio ; contador <= final ; contador = contador + 1){ cout << "\nIntroduce un nmero "; cin >> valor; suma = suma + valor; } media = suma / (final*1.0); cout << "\nLa media es " << media; }
172
for (contador = inicio ; contador <= final ; contador = contador + 1){ cout << "\nIntroduce un nmero "; cin >> valor; suma = suma + valor; } contador = inicio es la asignacin inicial de la variable contadora.
Si usamos como condicin
contador = contador + 1 aumenta en 1 el valor de contador en cada iteracin. Por abreviar, suele usarse contador++ en vez de contador = contador + 1 for (contador = inicio ; contador <= final ; contador++)
Podemos usar cualquier otro incremento:
173
false
true
174
int candidato, num_pares; num_pares = 0; for(candidato = -10; candidato <= 10; candidato++) { if (candidato % 2 == 0) num_pares++; } cout << "\nHay " << num_pares << " pares";
Este problema tambin se podra haber resuelto como sigue:
int num_pares, par; num_pares = 0; for (par = -10; par <= 10; par = par+2) num_pares++; cout << "\nHay " << num_pares << " pares";
175
for (i = 0; i < 9; i++) cout << "\nHola"; for (i = 9; i > 2; i--) cout << "\nHola"; for (i = 9; i >= 2; i--) cout << "\nHola";
-->
9 - 0 = 9
-->
9 - 2 = 7
-->
9 - 2 + 1 = 8
176
Nmero de iteraciones con incrementos cualesquiera. Si es del tipo contador <= final, tenemos que contar cuntos intervalos de longitud igual a incremento hay entre los valores inicio y final. El nmero de iteraciones ser uno ms. En el caso de que contador < final, habr que contar el nmero de intervalos entre inicio y final - 1. El nmero de intervalos se calcula a travs de la divisin entera. En resumen (considerando incrementos positivos): Si el bucle es del tipo contador <= final el nmero de iteraciones es (final - inicio) / incremento + 1 siempre y cuando sea inicio <= final. En otro caso, hay 0 iteraciones. Si el bucle es del tipo contador < final el nmero de iteraciones es (final - 1 - inicio) / incremento + 1 siempre y cuando sea inicio < final. En otro caso, hay 0 iteraciones. De forma anloga se realizan los clculos con incrementos negativos.
#
"
Usaremos los bucles for cuando sepamos, antes de entrar al bucle, el nmero de iteraciones que se tienen que ejecutar.
177
int i, suma_total; suma_total = 0; for (i = 1 ; i <= 10; i++) suma_total = suma_total + 3; cout << suma_total;
178
II.2.3.
Anidamiento de bucles
Dos bucles se encuentran anidados, cuando uno de ellos est en el bloque de sentencias del otro. En principio no existe lmite de anidamiento, y la nica restriccin que se debe satisfacer es que deben estar completamente inscritos unos dentro de otros.
ANIDAMIENTO PERMITIDO
ANIDAMIENTO NO PERMITIDO
179
#include <iostream> using namespace std; int main(){ const int TOPE = 3; int i, j; for (i = 1 ; i <= TOPE ; i++) { for (j = 1 ; j <= TOPE ; j++) cout << i << "*" << j << "=" << i*j << " cout << "\n"; } }
";
j=2
j=3
180
iteraciones = 0; suma = 0; for (i = 1 ; i <= n; i++){ for (j = 1 ; j <= n; j++){ suma = suma + j; iteraciones++; } }
Nmero de iteraciones: n2 Valor de la variable suma. Supongamos n= 5
1 1 1 1 1
+ + + + +
2 2 2 2 2
+ + + + +
3 3 3 3 3
+ + + + +
4 4 4 4 4
+ + + + +
5 5 5 5 5
suma = n
i=1
i=n
n2 + n 2
n3 + n2 2
181
iteraciones = 0; suma = 0; for (i = 1 ; i <= n; i++){ for (j = i ; j <= n; j++){ iteraciones++; suma = suma + j; } }
Nmero de iteraciones:
i=n
n + (n 1) + (n 2)+ +1 =
i=1
i=
n2 + n 2
< n2
1 + 2 + 3 + 4 + 2 + 3 + 4 + 3 + 4 + 4 +
5 5 5 5 5
i=n
suma = 5 5 + 4 4 + 3 3 + 2 2 + 1 1 =
i=1
i2 =
1 6
n(n + 1)(2n + 1)
182
Ejemplo. Imprimir en pantalla los divisores primos del entero n Algoritmo: Entradas: n Salidas: ninguna Descripcin: Recorrer todos los enteros menores que n Comprobar si el entero es primo. En dicho caso, comprobar si divide a n Ineciente. Es mejor el siguiente: Recorrer todos los enteros menores que n Comprobar si el entero divide a n. En dicho caso, comprobar si el entero es primo. Nota. Mejor an si empezamos desde n/2
183
n divisor_n
18 9
8 7
5 4
3 2
for (divisor_n = n/2 ; divisor_n > 1 ; divisor_n--){ if (n % divisor_n == 0){ es_primo = true; tope = sqrt(1.0*divisor_n); divisor_primo = 2; while (divisor_primo <= tope && es_primo){ if (divisor_n % divisor_primo == 0) es_primo = false; else divisor_primo++; } if (es_primo) cout << "\nEl primo " << divisor_n << " divide a " << n; } }
184
Ejemplo. El Teorema fundamental de la Aritmtica (Euclides 300 A.C/Gauss 1800) nos dice que podemos expresar cualquier entero como producto de factores primos. Imprimir en pantalla dicha descomposicin.
n 360 180 90 45 15 5 1
primo 2 2 2 3 3 5
// Dividir n por primo cuantas veces sea posible primo = 2; while (n % primo == 0){ cout << primo << " "; n = n / primo; }
Ahora debemos pasar al siguiente primo primo y volver a ejecutar el bloque anterior. Condicin de parada: n>=primo o bien n>1
Mientras n > 1 Dividir n por primo cuantas veces sea posible primo = siguiente primo mayor que primo
Cmo pasamos al siguiente primo?
185
primo++; Recorrer todos los enteros menores de primo hasta encontrar un divisor o hasta llegar a primo
Este recorrido implica un bucle, pero no es necesario. Hagamos simplemente primo++:
/* Mientras n > 1 Dividir n por primo cuantas veces sea posible primo++ */ primo = 2; while (n > 1){ while (n % primo == 0){ cout << primo << " "; n = n / primo; } primo++; }
Corremos el peligro de intentar dividir n por un valor primo que no sea primo? No. Por ejemplo, n=40. Cuando primo sea 4, podr ser n divisible por 4, es decir n%4==0? Despus de dividir todas las veces posibles por 2, me queda n=5 que ya no es divisible por 2, ni por tanto, por ningn mltiplo de 2. En general, al evaluar n%primo, n ya ha sido dividido por todos los mltiplos de primo. Nota. Podemos sustituir p++ por primo=primo+2 (tratando el primer caso primo=2 de forma aislada)
186
Ampliacin:
No hay forma de calcular el siguiente primo a uno dado (con una frmula directa). Como mucho, se puede establecer una cota del nmero de primos que hay menores a uno dado. Este problema est ligado (demostrado por Von Koch en 1901) a la Hiptesis de Riemann, uno de los problemas matemticos ms importantes sin resolver, y por el cual se ofrece una recompensa de 1 milln de dlares (por el Instituto Clay de Matemticas) Riemann fue un gran matemtico, discpulo de Gauss. Entre otras cosas, desarroll la geometra de Riemann, base fundamental de la teora de la relatividad de Einstein.
Ampliacin:
El problema de factorizar un nmero (expresarlo como producto de factores primos) es muy importante en la vida real. Hasta la fecha, no se ha conseguido construir (ni se espera que se consiga) un algoritmo que termine en un tiempo razonable para factorizar un nmero cuando ste es grande. Los conceptos de nmero grande y tiempo razonable se presentaron en la pgina 167. Dnde se aplica? En Criptografa (Cryptography) , para ocultar nuestros datos en Internet (por ejemplo, el nmero de la tarjeta de crdito). Mientras no se disee un algoritmo en este sentido, nuestros datos bancarios estn bien protegidos de los sniffers . Consultad:
http://angelrey.wordpress.com/2009/05/27/ numeros-primos-criptologia-y-codificacion/
Buscad en Internet: Criptografa asimtrica, Integer factorization, RSA numbers
187
II.2.4.
Particularidades de C++
C++ es un lenguaje muy verstil. A veces, demasiado II.2.4.1. Expresiones y sentencias son similares
El tipo bool como un tipo entero En C++, el tipo lgico es compatible con un tipo entero. Cualquier expresin entera que devuelva el cero, se interpretar como false. Si devuelve cualquier valor distinto de cero, se interpretar como true.
bool var_logica; var_logica = false; var_logica = (4>5); var_logica = 0; var_logica = (4<5); var_logica = true; var_logica = 2;
// Correcto: resultado false // Correcto: resultado 0 (false) :-O // Correcto: resultado true // Correcto: resultado 2 (true) :-O
Nota. Algunos compiladores, al ejecutar cout << false, imprimen en pantalla un cero, mientras que cout << true imprime un uno. La dualidad entre los tipos enteros y lgicos nos puede dar quebraderos de cabeza en los condicionales
188
int dato = 4; if (! dato < 5) cout << dato <<" es mayor o igual que 5"; else cout << dato <<" es menor de 5";
El operador ! tiene ms precedencia que <. Por lo tanto, la evaluacin es como sigue: ! dato < 5 (!dato) < 5 (dato vale 4) (!true) < 5 false < 5 0 < 5 true Imprime 4 es mayor o igual que 5! Solucin:
if (! (dato < 5)) cout << dato <<" es mayor o igual que 5"; else cout << dato <<" es menor de 5";
o mejor:
if (dato >= 5) cout << dato <<" es mayor o igual que 5"; else cout << dato <<" es menor de 5";
Consejo:
189
valor = 7;
Pero adems, devuelve un valor: el resultado de la asignacin. As pues, valor = 7 es una expresin que devuelve 7
valor = 5; if (valor = 7) <acciones if> // Siempre se ejecuta este bloque! else <acciones else> // Adems, valor se queda con 7 valor = 7 devuelve 7. Al ser distinto de cero, es true. Por tanto, se ejecuta el bloque if (y adems valor se ha modicado con 7) a = 7; if (a=0) cout << "\nRaz= " << -c/b; // Nunca se ejecuta else{ r1 = -b + sqrt(b*b -4*a*c) / (2*a) ; // Error lgico
190
C++ permite que una expresin constituya una sentencia Esta particularidad no da benecios salvo en casos muy especcos y sin embargo nos puede dar quebraderos de cabeza. As pues, el siguiente cdigo compila perfectamente:
int entero; 4 + 3;
C++ evala la expresin entera 4 + 3;, devuelve 7 y no hace nada con l, prosiguiendo la ejecucin del programa.
191
variable = 9; if (variable + 1 == 10) cout << variable << " es igual a 9"; cout << variable; // Imprime 9 // Correcto
variable = 9; if (variable++ == 10) cout << variable << " es igual a 9"; // "10 es igual a 9" cout << variable;
// "10"
Consejo:
192
II.2.4.2.
Bucles for con cuerpo vaco El siguiente cdigo no imprime los enteros del 1 al 20. Por qu?
193
Modicacin del contador nicamente mirando la cabecera de un bucle for sabemos cuantas iteraciones se van a producir (recordar lo visto en la pgina 176). Por eso, en los casos en los que sepamos de antemano cuntas iteraciones necesitamos, usaremos un bucle for. En otro caso, usaremos un bucle while do while. Para mantener dicha nalidad, es necesario respetar la siguiente restriccin: No se debe modicar el valor de la variable controladora, ni el valor nal dentro del cuerpo del bucle for
Sin embargo, C++ no impone dicha restriccin. Ser responsabilidad del programador. Ejemplo. Sumar los divisores de valor. Dnde est el fallo en el siguiente cdigo?:
suma = 0; tope = valor/2; for (divisor = 2; divisor <= tope ; divisor++) { if (valor % divisor == 0) suma = suma + divisor; divisor++; }
194
Supongamos que quiero salir de un bucle for cuando se produce una condicin. Cmo lo hago? Ejemplo. Escribe los enteros entre inf y sup, hasta llegar al primer mltiplo de 13:
cin >> inf; cin >> sup; for (entero = inf; entero <= sup; entero++) { cout << entero << " "; if (entero % 13 == 0) entero = sup + 1; }
Esto funciona pero es una chapuza. Mejor usamos un while:
// :-(
cin >> inf; cin >> sup; entero = inf; while (entero <= sup) && (entero % 13 != 0){ cout << entero << " "; }
195
es_primo = true; for (divisor = 2 ; divisor < valor ; divisor++) if (valor % divisor == 0){ es_primo = false; divisor = valor; // :-( }
Mejor la solucin que vimos en la pgina 166:
es_primo = true; divisor = 2 while (divisor < valor && es_primo){ if (valor % divisor == 0) es_primo = false; else divisor++; }
196
El bucle for como ciclo controlado por condicin Si bien en muchos lenguajes tales como PASCAL, FORTRAN o BASIC el comportamiento del ciclo for es un bucle controlado por contador, en C++ es un ciclo ms verstil, controlado por condicin. En el caso concreto de C++, su sintaxis es la siguiente:
donde, <sentencia inicial> es la sentencia que se ejecuta antes de entrar al bucle, <expresin lgica> es cualquier condicin que verica si el ciclo debe terminar o no, <sentencia nal> es la sentencia que se ejecuta antes de volver arriba para comprobar el valor de la expresin lgica. Por tanto, la condicin impuesta en el ciclo no tiene por qu ser de la forma contador < final, sino que puede ser cualquier tipo de condicin.
197
sentencia inicial
false
expresin lgica
true
sentencia final
198
es_primo = true; divisor = 2 while (divisor < valor && es_primo){ if (valor % divisor == 0) es_primo = false; else divisor++; }
Con un for quedara:
es_primo = true; divisor = 2 for (divisor = 2; divisor < valor && es_primo; divisor++) if (valor % divisor == 0) es_primo = false;
199
Ejemplo. Escribe los enteros entre inf y sup, hasta llegar al primer mltiplo de 13 (lo vimos en la pgina 194)
cin >> inf; cin >> sup; entero = inf; while (entero <= sup && entero % 13 != 0){ cout << entero << " "; entero++; }
cin >> inf; cin >> sup; for (entero = inf; entero <= sup && entero % 13 != 0 ; entero++) cout << entero << " ";
En los ejemplos anteriores
for (entero = inf; entero<=sup && entero%13 != 0 ; entero++) for (divisor = 2; divisor <= tope && es_primo; divisor++)
se ha usado dentro del for dos condiciones que controlan el bucle: La condicin relativa a la variable contadora. Otra condicin adicional. Este cdigo es completamente aceptable en C++.
200 $
En resumen, usaremos un bucle for en los casos en los que siempre exista: Una sentencia de inicializacin del contador Una condicin de continuacin que involucre al contador (pueden haber otras condiciones adicionales)
&
201
Pero ya puestos, puede usarse entonces, cualquier condicin dentro de la cabecera del for? S, pero no es muy recomendable. Ejemplo. Construir un programa que indique el nmero de valores que introduce un usuario hasta que se encuentre con un cero.
// Programa para contar el numero de valores que se // introducen hasta que se encuentra un cero. // --- Usando un ciclo while --#include <iostream> using namespace std; int main(){ int num_valores, valor; cin >> valor; num_valores = 0; while (valor != 0){ cin >> valor; num_valores++; } cout << "El nmero de valores introducidos es " << num_valores; }
202
// Programa para contar el nmero de valores que se // introducen hasta que se encuentra un cero. // --- Usando un ciclo for --#include <iostream> using namespace std; int main(){ int num_valores, valor; cin >> valor; for (num_valores=0; valor!=0; num_valores++) cin >> valor; cout << "\nEl nmero de valores introducidos es " << num_valores; }
Debemos evitar este tipo de bucles for en los que la(s) variable(s) que aparece en la condicin, no aparece en las otras dos expresiones.
203
Y ya puestos, podemos suprimir algunas expresiones de la cabecera de un bucle for? La respuesta es que s, pero hay que evitarlas SIEMPRE. Oscurecen el cdigo. Ejemplo. Sumar valores ledos desde la entrada por defecto, hasta introducir un cero.
int main(){ int valor, suma; cin >> valor; for ( ; valor!=0 ; ){ suma = suma + valor cin >> valor; }
204
II.2.4.3.
Existen otras sentencias en la mayora de los lenguajes que permiten alterar el ujo normal de un programa. En concreto, en C++ existen las siguientes sentencias:
goto
continue
break
exit
Durante los 60, qued claro que el uso incontrolado de sentencias de transferencia de control era la principal fuente de problemas para los grupos de desarrollo de software. Fundamentalmente, el responsable de este problema era la sentencia goto que le permite al programador transferir el ujo de control a cualquier punto del programa. Esta sentencia aumenta considerablemente la complejidad tanto en la legibilidad como en la depuracin del cdigo.
Referencias: Teorema de Bohm y Jacopini -1966-. Flow diagrams, Turing machines and languages only with two formation rules. Communications of the ACM, 1966. Vol. 9, No.5, pp. 366-371 Dijkstra, E.W. Goto statement considered harmful. Communications of the ACM, 1968. Vol. 11, No.3, pp. 147-148
205
Ampliacin:
Consultad el libro Code Complete de McConnell, disponible en la biblioteca. Incluye numerosos consejos y hbitos aconsejables de programacin. En el tema de Estructuras de Control incluye una referencia a un informe en el que se reconoce que un uso inadecuado de un break, provoc un apagn telefnico de varias horas en los 90 en NY.
En FP, no se permitir el uso de ninguna de las sentencias anteriores, excepto la sentencia break con el propsito aqu descrito dentro de la sentencia switch. En caso contrario, el Suspenso est garantizado.
Bibliografa recomendada para este tema: A un nivel menor del presentado en las transparencias: Segundo captulo de Deitel & Deitel A un nivel similar al presentado en las transparencias: Segundo y tercer captulos de Garrido. A un nivel con ms detalles: Captulos quinto y sexto de Stephen Prata. Tercer captulo de Lafore. Los autores anteriores presentan primero los bucles junto con la expresiones lgicas y luego los condicionales.
ndice alfabtico
algoritmo (algorithm), 6 aviso (warning), 52 biblioteca (library), 13 bit, 44 bucle controlado por condicin (condition-controlled loop), 142 bucle controlado por contador (counter controlled loop), 142 bucle post-test (post-test loop), 142 bucle pre-test (pre-test loop), 142 byte, 44 cdigo binario (binary code), 3 cdigo fuente (source code), 11 cadena de caracteres (string), 78 capas (layers), 123 carcter (char), 69 coma otante (oating point), 55 componentes lxicos (tokens), 19 condicin (condition), 91 constante (constant), 31 criptografa (cryptography), 185 dato (data), 6, 26 datos (data), 14 desbordamiento aritmtico (arithmetic overow), 52 diagramas de ujo (owchart), 97 errores en tiempo de compilacin (compilation error), 22 errores en tiempo de ejecucin (execution error), 23 errores lgicos (logic errors), 23 estructura condicional (conditional structure), 91 estructura repetitiva (iteration/loop), 142 estructura secuencial (sequential control ow structure), 91 evaluacin en ciclo corto (shortcircuit evaluation), 135 evaluacin en ciclo largo (eager evaluation), 135 exponente (exponent), 55 expresin (expression), 41 expresiones aritmticas (arithmetic expression), 62 ltro (lter), 144 ujo de control (control ow), 20 funcin (function), 39
hardware, 2
operador binario (binary operator), 38 identicador (name), 26 operador n-ario (n-ary operator), 38 implementacin de un algoritmo (aloperador unario (unary operator), gorithm implementation), 9 38 indeterminacin (undened), 59 palabra (word), 45 innito (innity), 59 parmetros (parameter), 39 iteracin (iteration), 143 principio de programacin - sencilgico (boolean), 81 llez (programming principle lenguaje de programacin (prosimplicity), 163 gramming language), 2, 3 principio de programacin - una lenguaje ensamblador (assembly nica vez (programming prinlanguage), 4 ciple - once and only once), 64 lenguajes de alto nivel (high level programa (program), 3 language), 4 programador (programmer), 2 literal (literal), 46 literales de cadenas de caracteres rango (range), 43 real (oat), 54 (string literals), 28 literales de caracteres (character li- reglas sintcticas (syntactic rules), 19 terals), 28 literales enteros (integer literals), 46 sentencia condicional (conditional literales lgicos (boolean literals), statement), 91 28 sentencias (sentence/statement), literales numricos (numeric lite13 rals), 28 sniffers, 185 mquina de turing (turing machine), software, 2 10 tipo de dato (data type), 26 mantisa (mantissa), 55 tipo enumerado (enumerated type), 88 niveles (tiers), 123 transformacin de tipo (casting), 66 notacin inja (inx notation), 38 notacin preja (prex notation), 38 usuario (user), 2 operador (operator), 39 valor (value), 26