Índice Tema
Página
Notas Preliminares Introducción a la Programación en C/ C++. Estructura de un programa en “C”:
Tipos de Datos- Declaración y asignación de variables Entrada / Salida – cin -cout Constantes. Operadores Iteraciones y decisiones Sentencia IF switch Sentencias de iteración: for while Do while FUNCIONES Pasaje de Parámetros Vectores o Vectores Unidimensionales Enum, Typedef Vectores Bidimensionales o Matrices Consideraciones importantes asociadas al uso de matrices: ORDENAMIENTO Ordenamiento Por Selección (Selection Sort) Ordenamiento BubbleSort. Ordenamiento Por Inserción Directa Método De Ordenamiento Por Inserción Binaria Ordenamiento por el Método Shell Ordenamiento Heap Sort Búsqueda de elementos en un Vector Búsqueda secuencial Búsqueda binaria Recursividad Fibonacci Torres de Hanoi Operaciones recursivas con Vectores Archivos en C++ Backtracking La vuelta del caballo El problema de las ocho reinas El Problema de la mochila (selección óptima) Ejemplo Backtraking Cadenas O Strings (swap, find first of,
Biblioteca conio - math Punteros Templates (introducción) Programación Visual Trabajos Prácticos Contenidos Asignatura EDI – Laboratiorio Programación C++ Prof. Abdala Pablo
3 3 4 5 6 7 7 8 9 10 11 12 13 14 16 16 18 19 21 22 23 24 25 26 27 29 29 29 31 33 34 35 37 40 42 45 47 48 53 55 56 70 74 103 139 2
Notas Preliminares
La Programación es el arte de dar soluciones a los problemas para que el equipo pueda ejecutarlas. La gran parte del esfuerzo en programar es buscar soluciones y refinarlas. refinarlas. A menudo, el problema es sólo comprender plenamente a través del proceso de programación la solución. Este apunte es para alguien que nunca ha programado antes, pero que está dispuesto a trabajar duro para aprender. Asimismo, le ayudará a comprender los principios y adquirir los conocimientos prácticos de la programación usando el lenguaje de programación C++. ¿Por qué C++? No se puede aprender a programar sin un lenguaje de programación, y C++ apoya directamente los principales conceptos y técnicas que se utilizan en el mundo real del software. C++ es uno de los lenguajes de programación más ampliamente utilizados. La mayoría de los conceptos de programación pueden utilizarse directamente en otros lenguajes, como C#, Fortran, PhP y Java. Por último, C++ es frecuente elegirlo como lenguaje para escribir código elegante y eficiente. La hipótesis fundamental es desear escribir los programas para el uso de otros, y de hacerlo con responsabilidad, proporcionando un buen nivel de calidad de todo el sistema, sis tema, es decir, lograr un muy buen nivel de profesionalismo. La programación se adquiere por escribir programas. En este tipo de programación es similar a la de otras actividades con un componente práctico. Las personas no pueden aprender a nadar, a tocar un instrumento musical, o conducir un coche solo con leer un libro. Todos debemos practicar. No se puede aprender a programar sin leer y escribir bastante código. Este apunte intenta centrase en ejemplos de código estrechamente vinculado con texto explicativo y diagramas. Esto es esencial, pero, por sí sola, no le brindará los conocimientos prácticos de programación. Para ello, se necesita hacer los ejercicios y acostumbrarse a las herramientas para escribir, compilar y ejecutar los programas.
Introducción a la Programación en C/ C++.
a) Introducción Teórica
Creador: Dennis Ritchie (Laboratorios Bell) en 1972, cuando trabajaba junto con Ken Thompson diseño del sistema operativo UNIX. El „C‟ se creó como herramienta para programadores, en consecuencia su principal objetivo es
ser un lenguaje útil. C++ es un lenguaje de programación creado a mediados de los años 1980 por BJarne Stroustrup. La intención intención de su creación creación fue fue extender al exitoso lenguaje de programación C con mecanismos que permitan la manipulación de objetos. EDI – Laboratiorio Programación C++ Prof. Abdala Pablo
3
Características: El “C” es un lenguaje de programación de “alto nivel” (alto nivel quiere decir “próximo al le nguaje humano”), pero con características de “bajo nivel” (bajo nivel= próximo al lenguaje
máquina). Es de ALTO de ALTO NIVEL porque es racional, estructurado y fácil de aprender. Es de BAJO NIVEL porque permite trabajar con “bits”, registros de la C.P.U. y posiciones de memoria. ¿Por qué el “C”? El lenguaje „C‟ es poderoso y flexible: la mayor parte del sistema operativo UNIX fue escrito en „C‟. Incluso están escritos en „C‟ los compiladores e intérpretes de otros lenguajes, como FO R-
TRAN, APL, PASCAL, LISP, LOGO y BASIC. El l enguaje „C‟ es “amistoso” porque es lo suficientemente estructurado para ejercer buenos hábitos de programación. Es el lenguaje de programación más utilizado por el programador de sistemas.
Estructura de un programa en “C”: El „C‟ es gráficamente:
CÓDIGO FUENTE: es el programa que nosotros escribimos, se graba con la extensión CPP CÓDIGO OBJETO: es el programa fuente pero traducido a lenguaje máquina (sucesión de ceros y unos), se graba con la extensión ex tensión OBJ PROGRAMA EJECUTABLE: EJECUTABLE: es el programa objeto más las “librerías del C”, se graba con la extensión EXE. Y no necesita el programa que hemos utilizado para crearlo, para poder ejecutarlo. El código Objeto que genera un compilador de “C”, es casi tan eficiente (rápido) como si lo
hubiéramos escrito en lenguaje ENSAMBLADOR (lenguaje de programación más próximo al lenguaje máquina).
El presente apunte está orientado a la construcción de programas sobre el compilador Borland C++ Builder 6.0 Para Borland C++ Builder 6.0 se utiliza: File – New- other- Console Wizard. EDI – Laboratiorio Programación C++ Prof. Abdala Pablo
4
Los programas en C++ consisten en una o más funciones. La única función que debe estar absolutamente presente es la denominada main, siendo la primera función que es llamada cuando comienza la ejecución del programa. Aunque main no forma técnicamente parte del lenguaje C, hay que tratarla como si lo fuera. Borland C++ Builder 6.0 #include Declaración de librerías int main() { Inicio de bloque // cuerpo del programa Return(0); } fin de bloque La forma general de un programa en C es:
El programa así escrito se denomina programa fuente y puede estar escrito en uno o varios archivos. Para que el programa pueda ser ejecutado se debe compilar y Ejecutar (run) con todas aque- llas funciones de la biblioteca que se necesiten.
Tipos de Datos
Todos los programas necesitan, en algún momento, almacenar números o datos ingresado por el usuario. Estos datos son almacenados en variables, y en C++ como en otros lenguajes estas variables deben tener un tipo.
EDI – Laboratiorio Programación C++ Prof. Abdala Pablo
5
bool
La palabra clave bool es el nombre para el tipo de datos Booleano en C++. Un tipo de datos Booleano puede tomar uno de dos valores: verdadero o falso, que en C++ se señalan con las palabras clave true y false respectivamente.
Ejemplo En el siguiente código, se inicializa una variable booleana con valor falso falso y luego se usa esta variable para controlar la ejecución de un bucle, la cual termina cuando la variable toma el valor verdadero: verdadero: bool listo = false; while( !listo ) { ... }
bool
Por lo general utiliza 1 byte de memoria, valores: true o false
Declaración y asignación de variables Para declarar una variable, basta con indicar su tipo y su nombre. Existen ciertas convenciones en cuanto al nombre de las variables. Algunos prefieren separar las partes de un nombre con '_', otros prefieren escribir una mayúscula para separarlas. Ejemplo: int mes;
o
int receta_del_mes;
Asignar un valor
Es posible asignar un valor a una variable al momento de declararla: int Mes = 12; También es posible declarar varias variables en una misma línea, pero en este caso, todas las variables de la línea tendrán el mismo tipo.
Otro Ejemplo double d; int i; short s; //... d=d+i; i=s*i;
int recetaDelMes = 12301, recetaDelAño = 45644545;
Entrada / Salida
Cout (C output) cout << "Texto: " << variable << "\n";
Se trata de un objeto global definido en "iostream.h "iostream.h". ". EDI – Laboratiorio Programación C++ Prof. Abdala Pablo
6
int entero = 10; char caracter = 'c'; char cadena[20] = "Hola"; float pi = 3.1416;
cout cout cout cout
<< << << <<
"entero=" << entero << "\n"; "caracter=" << caracter << endl; "cadena=" << cadena << endl; "pi=" << pi << endl;
Cin #include #include using namespace using namespace std;
int entero; string nombre; int main() { Cout<<”ingrese un número entero”<>entero; Cout <<”Ingrese Frase”<
}
Constantes. Las constantes se declaran, añadiendo la palabra const delante. Por ejemplo, para declarar una constante con valor 14: const int numero = 14;
Estas constantes no pueden ser modificadas a lo largo del programa. Por eso deben ser definidas al mismo tiempo que declaradas.
Operadores
aritméticos
lógicos
relacionales
Asignación
Lista de operadores aritméticos con su significado
! Not (no lógico) && And (y lógico) || Or (ó lógico)
== Igual a != No igual a > Mayor que < Menor que >= Mayor o igual que
= Asignación *= Asigna producto /= Asigna división %= Asigna resto (módulo) += Asigna suma -= Asigna diferencia (resta)
+ Suma EDI – Laboratiorio Programación C++ Prof. Abdala Pablo
7
- Resta * Producto / Cociente de una división % Resto de una división
<= Menor o igual que
<<= >>= &= ^= |=
Asigna desplazamiento izquierda Asigna desplazamiento derecha Asigna AND entre bits Asigna XOR entre bits Asigna OR entre bits
Ejemplo: x = 3; x += 3; x &= 3;
Iteraciones y decisiones DEFINICIÓN Las sentencias de decisión o también llamadas de CONTROL DE FLUJO son estructuras de control que realizan una pregunta la cual retorna verdadero o falso (evalúa una condición) y selecciona la siguiente instrucción a ejecutar dependiendo la respuesta o resultado. En algún momento dentro de nuestros programas, es preciso cambiar el flujo de ejecución de las instrucciones, es decir, el orden en que las instrucciones son ejecutadas. Muchas de las veces tenemos que tomar una decisión en cuanto a que se debe ejecutar basándonos en una respuesta de verdadero o falso (condición). La ejecución de las instrucciones incluyendo una estructura de control como el condicional funciona de esta manera: Las instrucciones comienzan a ejecutarse de forma secuencial (en orden) y cuando se llega a una estructura condicional, la cual está asociada a una condición, se decide qué camino tomar dependiendo siempre del resultado de la condición siendo esta falsa o verdadera.
Cuando se termina de ejecutar este bloque de instrucciones se reanuda la ejecución en la instrucción siguiente a la de la condicional.
Sentencia IF
La instrucción if es, if es, por excelencia, la más utilizada para construir estructuras de control de flujo. SINTAXIS Primera Forma if (condición) { Set de instrucciones }
Siendo "condición" el el lugar donde se pondrá la condición o pregunta pregunta que se tiene que cumplir para que sea verdadera la sentencia y así proceder a realizar el "conjunto de sentencias" o código contenido dentro de la sentencia. Segunda Forma EDI – Laboratiorio Programación C++ Prof. Abdala Pablo
8
if (condición) { Conjunto de sentencias } else { Conjunto de sentencias 2 }
//PARTE VERDADERA
//Parte FALSA
La forma mostrada anteriormente muestra la unión de la parte "VERDADERA" con la nueva secuencia la cual es la parte "FALSA" de la sentencia de decisión "IF" en la cual esta compuesta por el: else { Conjunto de sentencias 2
//Parte FALSA
}
"else" o "De lo contrario" indica al lenguaje que de lo contrario al no ser verdadera o no se cumpla la parte verdadera entonces realizara el "set de instrucciones 2".
la palabra
EJEMPLOS DE SENTENCIAS IF... Ejemplo 1: #include #pragma hdrstop #include #pragma argsused int numero; int main(int argc, char* argv[]) { cout<<"ingrese numero"; cin>>numero; if(numero == 0) //La condición indica que tiene que ser igual a Cero { cout<<"El Numero Ingresado es Igual a Cero"; } system(“pause”); return 0; }
Ejemplo 2:
if(numero > 0) // la condición indica que tiene que ser mayor a Cero { cout<<"El Numero Ingresado es Mayor a Cero"; }
Ejemplo 3: if(numero < 0) // la condición indica que tiene que ser menor a Cero { cout<<"El Numero Ingresado es Menor a Cero"; }
EDI – Laboratiorio Programación C++ Prof. Abdala Pablo
9
Ahora uniremos todos estos ejemplos para formar un solo programa mediante la utilización de la sentencia "Else" e introduciremos el hecho de que se puede escribir en este espacio una sentencia if ya que podemos ingresar cualquier tipo de código dentro de la sentencia escrita después de un Else. Ejemplo 4: if(numero == 0) //La condición indica que tiene que ser igual a Cero { cout<<"El Numero Ingresado es Igual a Cero"; } else { if(numero > 0) // la condición indica que tiene que ser mayor a Cero { cout<<"El Numero Ingresado es Mayor a Cero"; } else { if(numero < 0) // la condición indica que tiene que ser menor a Cero { cout<<"El Numero Ingresado es Menor a Cero"; } } }
Sentencia switch
switch es otra de las instrucciones que permiten la construcción de estructuras de control. A diferencia de if , para controlar el flujo por medio de una sentencia switch se debe de combinar con el uso de las sentencias case y break . Notas: cualquier número de casos a evaluar por switch así como la sentencia default son opcionales. La sentencia switch es muy útil en los casos de presentación pr esentación de menús. Sintaxis: switch (condición) { case primer_caso: bloque de instrucciones 1 break; case segundo_caso: bloque de instrucciones 2 break; case caso_n: bloque de instrucciones n break; default: bloque de instrucciones por defecto }
Ejemplo 1 #include #pragma hdrstop
EDI – Laboratiorio Programación C++ Prof. Abdala Pablo
10
#include #pragma argsused int numero; int main(int argc, char* argv[]) { cout<<"ingrese numero"; cin>>numero; switch (numero) { case 0: cout << "numero es cero"; } system(“pause”); return 0; }
Ejemplo 2 switch (opcion) { case 0: cout << "Su opción es cero"; break; case 1: cout << "Su opción es uno"; break; case 2: cout << "Su opción es dos"; }
Ejemplo 3 switch (opcion) { case 1: cout << "Su opción es 1"; break; case 2: cout << "Su opción es 2"; break; case 3: cout << "Su opción es 3"; break; default: cout << "Elija una opción entre 1 y 3"; }
Sentencias de iteración Definición Las Sentencias de Iteración o Ciclos son estructuras de control que repiten la ejecución de un grupo de instrucciones. Básicamente, una sentencia de iteración es una estructura de control condicional, ya que dentro de la misma se repite la ejecución de una o más instrucciones mientras o hasta que una a condición específica se cumpla. Muchas veces tenemos que repetir un número definido o indefinido de veces un grupo de instrucciones por lo que en estos casos utilizamos este tipo de sentencias. En C++ los ciclos o bucles se construyen por medio de las sentencias for, for, while y do - while. while. La sentencia for es útil para los casos en donde se conoce de antemano el número de veces que una o más sentencias han de repetirse. Por otro lado, la sentencia while es útil en aquellos casos en donde no se conoce de antemano el número de veces que una o más sentencias se tienen que repetir. Sentencias For for(contador; final; incremento) { Código a Repetir; }
donde: EDI – Laboratiorio Programación C++ Prof. Abdala Pablo
11
1. contador es una variable numérica 2. final es la condición que se evalúa, o sea, el valor final para contador 3. incremento es el valor que se suma o resta al contador Ejemplo 1: #include #include #pragma hdrstop #pragma argsused int main(int argc, char* argv[]) { for(int i=1; i<=10; i++) { cout<<"Hola Mundo"<
Esto indica que el contador "i" inicia desde 1 y finaliza cuando el contador "i" sea menor o igual a 10 ( en este caso llegará hasta 10) e "i++" realiza la sumatoria por unidad lo que hace que el for y el contador se sumen. repitiendo 10 veces "HOLA MUNDO" en pantalla. Ejemplo 2: #include #include #pragma hdrstop #pragma argsused int main(int argc, char* argv[]) { for(i=10; i>=0; i--) { cout<<"Hola Mundo"<
Este ejemplo hace lo mismo que el primero, salvo que el contador se inicializa a 10 en lugar de 1; y por ello cambia la condición que se evalúa así como que el contador se decremento en lugar de ser incrementado.
Sentencia while while(condición) { código a Repetir }
donde: 1. condición es la expresión a evaluar EDI – Laboratiorio Programación C++ Prof. Abdala Pablo
12
Ejemplo 1: #include #include #pragma hdrstop #pragma argsused int main(int argc, char* argv[]) { int contador = 0; while(contador<=10) { contador=contador++; cout<<"Hola Mundo"<
El contador Indica que hasta que este llegue a el total de 10 entonces se detendrá y ya no se realizará el código contenido dentro de la sentencia while, while, de lo contrario mientras el "contador" sea menor a 10 entonces e ntonces el código contenido se ejecutará desplegando hasta 10 veces "Hola Mundo" en pantalla. Sentencia do - while
La sentencia do es usada generalmente en cooperación con while para garantizar que una o más instrucciones se ejecuten al menos una vez. Por ejemplo, en la siguiente construcción no se ejecuta nada dentro del ciclo while, while, el hecho es que el contador inicialmente vale cero y la condición para que se ejecute lo que está dentro del while es "mientras el contador sea mayor que diez". Es evidente que a la primera evaluación hecha por while la condición deja de cumplirse. int contador = 0; while(contador > 10) { contador ++; cout<<"Hola Mundo"; }
Al modificar el segmento de código anterior usando do tenemos: #include #include #pragma hdrstop #pragma argsused int main(int argc, char* argv[]) { int contador = 0; do { contador ++; cout<<"Hola Mundo"; } while(contador > 10); system(“pause”); system(“pause”);
EDI – Laboratiorio Programación C++ Prof. Abdala Pablo
13
return 0; }
Observe cómo en el caso de do la condición es evaluada al final en lugar de al principio del bloque de instrucciones y, por lo tanto, el código que le sigue al do se ejecuta al menos la primera vez. FUNCIONES Definición de una Función
Uno de los conceptos actualmente utilizados en el mundo del software, es la de Modularidad. Se ha dicho que la Modularidad, es el atributo individual del software que permite a un programa ser intelectualmente manejable. El software monolítico (es decir, un programa compuesto de un único módulo) no puede ser fácilmente abarcado por un lector. El número de caminos de control, control, la expansión de referencias, referencias, el número de variables y la complejidad global podrían hacer imposibles su correcta compresión.
Estructura de una función: tipo_de_func ion
nombre_de_la_f uncion (argumentos)
{ Cuerpo de la Función return(variable ó valor); /* opcional solo cuando la función es de tipo void*/ }
Donde el tipo_de_funcion corresponde al tipo de datos que retorna ó devuelve dicha función, la cual puede ser del tipo entero ( int ) , flotante (float), carácter (char ) etc, además es importante aclarar que aquellas funciones que no son definidas, por defecto son de tipo entero. argumentos: corresponde a valores recibidos por la función, los cuales también se deben definir de un tipo determinado, como si se tratase de una definición de variable común y corriente, cabe destacar además que no necesariamente una función debe recibir un valor, si no que esta puede estar vacía. Cuerpo de la función: función: Corresponde al código fuente que se encargará de realizar el proceso, aquí se deberán declarar variables si fuese necesario, dichas variables son denominadas variables locales porque pertenecen a esa función y sólo existen cuando el compilador toma dicha función, dejando de existir cuando el compilador termina de ejecutar dichas líneas de código. Existen las variables globales, definidas en el programa principal, antes del main(), las cuales pueden ser utilizadas en cualquier parte del programa, incluyendo las funciones. return(variable return(variable ó valor): Corresponde al valor que retornará dicha función donde fue llamada, es importante destacar que una variable ó valor único. Más adelante veremos como EDI – Laboratiorio Programación C++ Prof. Abdala Pablo
14
retornar variables que tengan asociados más de un valor, por lo que por el momento sólo nos quedaremos con la proposición inicial. Algunas veces no necesitaremos que las l as funciones nos retornen un valor, por lo que podrepodr emos omitir esta sentencia. Ejemplo Crear un programa utilizando funciones donde se sumen dos números. En este programa existen variables globales (x e y) y variables locales (result en el cuerpo del programa principal; y las variables a y b en la función suma. Las variables locales sólo tienen existencia durante la ejecución de la respectiva función. Pueden existir variables locales con el mismo nombre en distintas funciones las cuales estarán haciendo referencia a posiciones distintas de memoria. #include #include #pragma hdrstop #pragma argsused int x,y; int suma(int a,int b); // Todas las funciones se declaran antes que el // main(), para que puedan ser reconocidas por el //compilador y utilizadas en cualquier parte del //programa int main(int argc, char* argv[]) { int result; cout<<"Ingrese cout<<"Ingrese el primer valor "; cin>>x; cout<<"Ingrese el segundo valor "; cin>>y; result=suma(x,y); result=suma(x,y); //se ingresan las variables adecuadas como parámetros y el //valor devuelto por la función es asignada a la variable result . cout<<"La suma es : "<
Otro Ejemplo Escribir un programa que permita al usuario elegir el cálculo del área de cualquiera de las figuras geométricas: círculo, cuadrado, rectángulo o triángulo mediante funciones. #include #pragma hdrstop #include #include #include EDI – Laboratiorio Programación C++ Prof. Abdala Pablo
15
#pragma argsused int n,r,b,a,b1,b2,a2; float circulo(int r); void carga(); float triangulo (int b, int a); float cuadrado (int b1); float rectangulo (int b2, int a2); int main(int argc, char* argv[]) { carga(); system(“pause”); return 0; } void carga () { do { clrscr(); cout<<"1. circulo"<>n; switch (n) { case 1:cout<<"Opcion 1=circulo"<>r; cout<<"El superficie del circulo es:"<>b; cout<<"Ingrese la altura del triangulo:"<>a; cout<<"La superficie del triangulo es:"<
break; case 3:cout<<"Opcion 3=cuadrado"<>b1; cout<<"La superficie del cuadrado es:"<>b2; cout<<"Ingrese la altura del rectangulo:"<>a2; cout<<"La superficie del rectangulo es:"<
break; } system(“pause”); } EDI – Laboratiorio Programación C++ Prof. Abdala Pablo
16
while (n !=5); } float circulo(int r) { return ((float)(3.14*r*r)); } float triangulo(int a, int b) { return ((float) (b*a)/2); } float cuadrado (int b1) { return ((float) (b1*b1)); } float rectangulo (int b2, int a2) { return ((float)(b2*a2)); }
Paso de parámetros C++ permite dos tipos de paso de parámetros: por valor (también denominado por copia, en terminología de C++), y por referencia. referencia. Paso de parámetros por referencia El paso de parámetros por referencia se utiliza cuando se le quiere pasar una variable a una función o procedimiento, de manera que los cambios que se le hagan a esa variable dentro de la función sean repercutidos después en el resto del programa. #include #pragma hdrstop #include #pragma argsused void imprimeValor(int *n); int main(int argc, char* argv[]) { int contador = 0; contador++; cout<<"el valor del contador es:" << contador<
Paso de parámetros por valor
EDI – Laboratiorio Programación C++ Prof. Abdala Pablo
17
El paso de parámetros por valor valor se utiliza cuando cuando se le quiere simplemente pasar un dato a una función o procedimiento, sin más repercusiones en el resto del d el programa. El paso de parámetros por valor admite dos variantes, que es necesario utilizar en función del tipo de dato del parámetro que se quiera pasar. Para los tipos de datos simples, esto es, Entero y Real, el paso de parámetros por valor consiste básicamente en poner el nombre del tipo y el nombre del parámetro formal a continuación. Para su invocación, se indica el nombre de la variable o un valor literal. #include #pragma hdrstop #include #pragma argsused void imprimeValor(); int main(int argc, char* argv[]) { int contador = 0; contador++; cout<<"el valor del contador es:" << contador<
enum Sintaxis: enum nombre {lista-de-nombres} variable-lista; La palabra clave enum es usada para crear un tipo enumerador llamado nombre que consiste de los elementos en lista-de-nombre. La variable-lista es opcional, y puede ser usada para crear instancias de tipo nombre junto con la declaracion. Por ejemplo, el siguiente código crea un tipo enumerador para colores: enum ColorT {rojo, naranja, amarillo, verde, azul, indigo, violeta}; ... ColotT c1 = indigo; if( c1 == indigo ) { cout << "c1 es índigo" << endl; }
En el ejemplo anterior, el efecto de enumeración es introducir nuevas constantes llamadas ro jo, naranja, amarillo, etc. Por defecto, estas constantes son asignadas consecutivamente como enteros empezando en cero. Puedes cambiar los valores de estas constantes, como es mostrado en el siguiente ejemplo: EDI – Laboratiorio Programación C++ Prof. Abdala Pablo
18
int main(int argc, char* argv[]) { enum ColorT { rojo = 10, azul = 166, verde }; //verde toma el valor de azul+1 ColorT c = azul; cout << "c es " << c << endl; system(“pause”); return 0; }
Se pone typedef para que funcione en compiladores C y C++ a la par: typedef enum ColorT { rojo = 10, azul = 15, verde } ColorT; ColorT c = verde; cout << "c es " << c << endl;
Vectores Unidimensionales Un vector o vector vector es un conjunto conjunto de datos del mismo tipo que se puede representar representar de la siguiente manera:
Dicho vector podría haber sido representado por una una definición de 8 variables de tipo char. Cabe destacar además que los vectors comienzan en el el índice 0. Para declarar un vector se sigue la siguiente sintaxis. Tipo_Dato
Nombre_Variable[Cantidad_Elementos];
Ejemplo: Ingresar 10 elementos utilizando vectors y mostrar mo strar el mayor. Entrada: 10 enteros Salida: El mayor de los 10 enteros #include #include #include #pragma hdrstop
EDI – Laboratiorio Programación C++ Prof. Abdala Pablo
19
#pragma argsused void cargar(); void obtener_mayor(int vector[10]); void mostrar_mayor(int mayor); int main(){ cargar(); system(“pause”); return 0; } void cargar(){ int vector[10],i; vector[10],i; for(i=0;i<10;i++){ clrscr(); cout<<"Ingrese el numero en la posicion "<>vector[i]; } obtener_mayor(vector); } void obtener_mayor(int vector[10]){ int mayor=0,i; for(i=0;i<10;i++){ if(vector[i]>mayor){ mayor=vector[i]; } } mostrar_mayor(mayor); } void mostrar_mayor(int mayor){ clrscr(); cout<<"El mayor numero ingresado es: "<
Vectors Bidimensionales o Matrices Un vector multidimensional, es un vector de vectors. Una matriz es un vector bidimensional. Declaración de una matriz: int vector[filas][columnas]; vector[filas][columnas]; int a[5][5],m[10] [4],i,j,f,c // Ejemplo: int vector[5][3];
nombre del vector y dimensión
Su representación es:
EDI – Laboratiorio Programación C++ Prof. Abdala Pablo
20
Lectura de una Matriz (llenado): for (i=0; i<5;i++) { for (j=0; j < 5 ; j=j+1) { Ingrese eleCout<<” \n mento A[“<>a[i][j]; } }
Cada elemento se referencia a través del nombre y su ubicación en el con junto, por ejemplo: { a[4][2]=10; // Se almacena en la fila 5, columna 3 el valor 10 m[0][5]=0; // Se almacena almace na en la fila uno, columna 6 el valor 0 if (a[i][j] == a[j][i] ) // se consulta si el valor ubicado en la fila i-1 y columna j-1 es igual al elemento // almacenado en la fila j-1 y columna i-1 }
Consideraciones importantes asociadas al uso de matrices: • • • •
El procesamiento de todos los elementos requiere como mínimo mínimo dos ciclos iterativos. iterativos. Dentro de los ciclos se deben realizar las operaciones específicas específicas que se solicitan. solicitan. Una matriz cuadrada es aquella en el numero de filas es igual al número de columnas. Entre las aplicaciones aplicaciones más importantes importantes de las matrices matrices está la posibilidad posibilidad de resolver sis-
temas de ecuaciones con n variables. Las matrices cuadradas tienen algunas características importantes asociadas a la ubicación de sus elementos en el conjunto. Se puede hablar de diagonal principal, diagonal secundaria, triangular superior, triangular inferior, por mencionar algunas.
ORDENAMIENTO Es la operación de arreglar los registros de una tabla en algún orden secuencial de acuerdo a un criterio de ordenamiento. El ordenamiento se efectúa con base en el valor de algún campo en un registro. El propósito principal p rincipal de un ordenamiento es el de facilitar las búsquedas de los miembros del conjunto ordenado.
EDI – Laboratiorio Programación C++ Prof. Abdala Pablo
21
El ordenar un grupo de datos significa s ignifica mover los datos o sus referencias para que queden en una secuencia tal que represente un orden, el cual puede ser numérico, alfabético o incluso alfanumérico, ascendente o descendente. Ordenamiento Por Por Selección Selección (Selection Sort) • Busca el elemento más pequeño de la lista. • Intercambia con el elemento ubicado en la primera posición de la lista. • Busca el segundo elemento más pequeño de la lista. • Lo intercambia con el elemento que ocupa la segunda posición en la lista. • Repite este proceso hasta que haya ordenado toda la lista.
ANÁLISIS DEL ALGORITMO. • Requerimientos de Memoria: Al igual ig ual que el ordenamiento burbuja, este algoritmo sólo nece-
sita una variable adicional para realizar los intercambios.
• Tiempo de Ejecución: El ciclo externo se ejecuta n veces para una lista de n elementos. Cada
búsqueda requiere comparar todos los elementos no clasificados. Ventajas:
• Fácil implementación. • No requiere memoria adicional. • Rendimiento constante: poca diferencia entre el peor y el mejor caso.
Desventajas: • Lento. • Realiza numerosas comparaciones #include #pragma hdrstop #include #include #include #include #pragma argsused void seleccionsort(int vector[15],int tamano); void mostrarVector(int[], int); int _main( ) { const int tamano = 15; int vector[tamano] = {25,17,13,16,41,32,12,115,95,84,54,63,78,21,10}; int i; cout << "vector sin ordenar\n" ; mostrarVector(vector,tamano); cout << "vector ordenado\n" ; seleccionsort(vector,tamano); mostrarVector(vector,tamano); system(“pause”); return 0; } void seleccionsort (int A[], int n) { int min,i,j,aux; min,i,j,aux;
EDI – Laboratiorio Programación C++ Prof. Abdala Pablo
22
for (i=0; i A[j]) {min=j; aux=A[min]; A[min]=A[i]; A[i]=aux ;} } } } void mostrarVector( int vector[], int tamano) { for (int i = 0 ; i < tamano ; i++) cout << vector[i] << "/"; cout<<"\n"; }
Ordenamiento BubbleSort o Burbuja o Intercambio Directo. Es un sencillo algoritmo de ordenamiento. Funciona revisando cada elemento de la lista que va a ser ordenada con el siguiente, intercambiándolos de posición si están en el orden equivocado. Es necesario revisar varias veces toda la lista hasta que no se necesiten más intercambios, lo cual significa que la lista está ordenada. Este algoritmo obtiene su nombre de la forma con la que suben por la lista los elementos durante los intercambios, como si fueran pequeñas "burbujas". También es conocido como el método del intercambio directo. Dado que solo usa comparaciones para operar elementos, se lo considera un algoritmo de comparación, siendo el más sencillo de implementar. i mplementar. #include #pragma hdrstop #include #include #pragma argsused void BubbleSort(int vector[15],int tamano); void mostrarVector(int[], int); int main(int argc, _TCHAR* argv[]) { const int tamano = 15; int vector[tamano] = {20,17,13,16,41,32,12,115,95,84,54,63,78,21,10}; int i; cout << "vector sin ordenar\n" ; mostrarVector(vector,tamano); cout << "vector ordenado\n" ; BubbleSort(vector,tamano); mostrarVector(vector,tamano); system(“pause”); return 0; } //----------------------------------------------------------------void BubbleSort(int A[],int n) {int i, j, inc, temp, k,item ; for (i = 1; i
EDI – Laboratiorio Programación C++ Prof. Abdala Pablo
23
for (j = 0; j A[j + 1]) { temp = A[j]; A[j] = A[j + 1] ; A[j + 1] = temp; } } } } void mostrarVector( int vector[], int tamano) { for (int i = 0 ; i < tamano ; i++) cout << vector[i] << "/"; cout<<"\n"; }
Ordenamiento Por Inserción Directa DESCRIPCIÓN. El algoritmo de ordenación por el método de inserción directa es un algoritmo relativamente sencillo y se comporta razonablemente bien en gran cantidad de situaciones. s ituaciones. Completa la tripleta de los algoritmos de ordenación más básicos y de orden de comple jidad cuadrático, junto con SelectionSort y BubbleSort. Se basa en intentar construir una lista ordenada en el interior del array a ordenar. De estos tres algoritmos es el que mejor resultado da a efectos prácticos. Realiza una cantidad de comparaciones bastante equilibrada con respecto a los intercambios, y tiene un par de características que lo hacen aventajar a los otros dos en la mayor parte de las situaciones. Este algoritmo se basa en hacer comparaciones, así que para que realice su trabajo de ordenación son imprescindibles dos cosas: un array o estructura similar de elementos comparables y un criterio claro de comparación, tal que dados dos elementos nos diga si están en orden o no. • En cada iteración del ciclo externo los elementos 0 a i forman una lista ordenada
ANÁLISIS DEL ALGORITMO. • Estabilidad: Este algoritmo nunca intercambia registros con claves iguales. Por lo tanto es
estable.
• Requerimientos de Memoria: Una variable adicional ad icional para realizar los intercambios. • Tiempo de Ejecución: Para una lista den elementos el ciclo externo se ejecuta n 1 veces. El
ciclo interno se ejecuta como máximo una vez en la primera iteración, 2 veces en la segunda, 3 veces en la tercera, etc. Ventajas: EDI – Laboratiorio Programación C++ Prof. Abdala Pablo
24
• Fácil implementación. • Requerimientos mínimos de memoria. Desventajas: • Lento. • Realiza numerosas comparaciones.
Este también es un algoritmo lento, pero puede ser de utilidad para listas que están ordenadas o semi ordenadas, porque en ese caso realiza muy pocos desplazamientos. void insercionDirecta(int A[],int n) { int i,j,v; for (i = 1; i < n; i++) { v = A[i]; j=i -1; while (j >= 0 && A[j] > v) { A[j + 1] = A[j]; j--; }A[j+ 1] = v ; } }
Método De Ordenamiento Por Inserción Binaria El método de ordenación por 'inserción binaria'' es una mejora del método de inserción directa. Para lograr esta mejora se recurre a una búsqueda binaria en lugar de una búsqueda secuencial para insertar un elemento en la parte izquierda del vector, que ya se encuentra ordenado. El resto del procedimiento es similar al de inserción directa, es decir, se repite este mismo procedimiento desde el segundo término hasta el último elemento. void insercionBinaria(int A[],int n) { int i,j,aux,izq,der,m; i,j,aux,izq,der,m; for(i=1;i=izq) { A[j+1]=A[j]; j=j-1; }A[izq]=aux; }
EDI – Laboratiorio Programación C++ Prof. Abdala Pablo
25
}
Ordenamiento por el Método Shell El método Shell es una versión mejorada del método de inserción directa. Este método también se conoce con el nombre de inserción con incrementos decrecientes. En el método de ordenación por inserción directa cada elemento se compara para su ubicación correcta en el vector, con los elementos que se encuentran en la parte izquierda del mismo. Si el elemento a insertar es más pequeño que el grupo de elementos que se encuentran a su izquierda, es necesario efectuar entonces varias comparaciones antes de su s u ubicación. Shell propone que las comparaciones entre elementos se efectúen con saltos de mayor tamaño pero con incrementos decrecientes, así, los elementos quedarán ordenados en el vector más rápidamente. El Shell sort es una generalización del ordenamiento por inserción, teniendo en cuenta dos observaciones: 1. El ordenamiento por inserción es eficiente si la entrada está es tá "casi ordenada". 2. El ordenamiento por inserción es ineficiente, en general, porque mueve los valores sólo una posición cada vez. El algoritmo Shell sort mejora el ordenamiento por inserción comparando elementos separados por un espacio de varias posiciones. Esto permite que un elemento haga "pasos más grandes" hacia su posición esperada. Los pasos múltiples sobre los datos se hacen con tamaños de espacio cada vez más pequeños. El último paso del Shell sort es un simple ordenamiento por inserción, pero para entonces, ya está garantizado que los datos del vector están casi ordenados. El Shell sort lleva este nombre en honor a su inventor, Donald Shell, que lo publicó en 1959. #include #pragma hdrstop #include #include #include #include void ordenShell(int vector[15],int tamano); void mostrarVector(int[], int); #pragma argsused int _tmain(int argc, _TCHAR* argv[]) { const int tamano = 15; int vector[tamano] = {25,17,13,16,41,32,12,115,95,84,54,63,78,21,10}; int i; cout << "vector sin ordenar\n" ; mostrarVector(vector,tamano); cout << "vector ordenado\n" ; ordenShell(vector,tamano); mostrarVector(vector,tamano); system(“pause”);
EDI – Laboratiorio Programación C++ Prof. Abdala Pablo
26
return 0; } //--------------------------------------------------------------------------void ordenShell(int A[],int n) {int i, j, inc, temp; for(inc = 1 ; inc 0) { for (i=inc; i < n; i++) { j = i; temp = A[i]; while ((j >= inc) && (A[j-inc] > temp)) { A[j] = A[j - inc]; j = j - inc; } A[j] = temp; } inc/= 2; } } void mostrarVector( int vector[], int tamano) { for (int i = 0 ; i < tamano ; i++) cout << vector[i] << "/"; cout<<"\n"; }
Ordenamiento Heap Sort El ordenamiento por montículos (Heap sort) es un algoritmo de ordenación no recursivo, no estable. Este algoritmo consiste en almacenar todos los l os elementos del vector a ordenar en un montículo mo ntículo (heap), y luego extraer el nodo que queda como nodo raíz del montículo (cima) en sucesivas iteraciones obteniendo el conjunto ordenado. Basa su funcionamiento en una propiedad de los montículos, por la cual, la cima contiene siempre el menor elemento (o el mayor, según se haya definido el montículo) de todos los almacenados en él. El significado de heap en ciencia computacional es el de una cola de prioridades (priority queue). Tiene las siguientes características: Un heap es un vector de n posiciones ocupado o cupado por los elementos de la cola. (Nota: se utiliza un vector que inicia en la posición 1 y no en cero, de tal manera que al implementarla en C se tienen n+1 posiciones en el vector.) Se mapea un árbol binario de tal manera en el vector que el nodo en la posición i es el padre de los nodos en las posiciones (2*i) y (2*i+1). El valor en un nodo es mayor o igual a los valores de sus hijos. Por consiguiente, el nodo padre tiene el mayor valor de todo su subárbol. PROCEDIMIENTO Heap Sort consiste esencialmente en: EDI – Laboratiorio Programación C++ Prof. Abdala Pablo
27
• Convertir el vector en un heap • Construir un vector ordenado de atrás hacia adelante (mayor a menor) repitiendo los siguien-
tes pasos:
• Sacar el valor máximo en el heap (el de la posición 1) • Poner ese valor en el vector ordenado • Reconstruir el heap con un elemento menos • Utilizar el mismo vector para el heap y el vector ordenado #include #pragma hdrstop #include #include #include #include #pragma argsused void heapsort(int vector[15],int tamano); void mostrarVector(int[], int); int _tmain(int argc, _TCHAR* argv[]) { const int tamano = 15; int vector[tamano] = {0,17,13,16,41,32,12,115,95,84,54,63,78,21,10}; int i; cout << "vector sin ordenar\n" ; mostrarVector(vector,tamano); cout << "vector ordenado\n" ; heapsort(vector,tamano); mostrarVector(vector,tamano); system(“pause”); return 0; } //-----Este método comienza en la posición 1!!! --------------------void heapsort(int A[],int n) {int i, j, inc, temp, k,item ; for(k=n;k>0;k--) { for(i=1;i<=k;i++) { item=A[i]; j=i/2; while(j>0 && A[j]
EDI – Laboratiorio Programación C++ Prof. Abdala Pablo
28
cout<<"\n"; }
Búsqueda de elementos en un Vector La búsqueda de un elemento dentro de un array es una de las operaciones más importantes en el procesamiento de la información, y permite la recuperación de datos previamente almacenados. El tipo de búsqueda se puede clasificar como interna o externa, según el lugar en el que esté almacenada la información (en memoria o en dispositivos externos). Todos los algoritmos de búsqueda tienen dos finalidades: - Determinar si el elemento buscado se encuentra en el conjunto en el que se busca. - Si el elemento está en el conjunto, hallar la posición en la que se encuentra. En este apartado nos centramos en la búsqueda interna. Como principales algoritmos de búsqueda en arrays tenemos la búsqueda secuencial, la binaria y la búsqueda utilizando tablas de hash. Búsqueda secuencial Consiste en recorrer y examinar cada uno de los lo s elementos del array hasta encontrar el o los elementos buscados, o hasta que se han mirado todos todo s los elementos del vector. void secuencia(int a[n1],int k1) { int cual,x1; bool ubicado=false; cout<<"Que elemento se buscara? : ";cin>>cual; cout<
Búsqueda binaria La búsqueda binaria sólo se puede implementar imp lementar si el vector está ordenado. La idea consiste en ir dividiendo el vector en mitades. Por Po r ejemplo supongamos que tenemos este vector: int vector[10] =
{2,4,6,8,10,12,14,16,18,20}; {2,4,6,8,10,12,14,16,18,20};
La clave que queremos buscar es 6. El algoritmo funciona de la siguien s iguien manera 1. Se determinan un indice arriba y un indice abajo, Iarriba=0 e Iabajo=9 respectivamente. 2. Se determina un indice central, Icentro = (Iarriba + Iabajo)/2, en este caso quedaría Icentro = 4. EDI – Laboratiorio Programación C++ Prof. Abdala Pablo
29
3. Evaluamos si vector[Icentro] es igual a la clave de busqueda, si es igual ya encontramos la clave y devolvemos Icentro. 4. Si son distintos, evaluamos si vector[Icentro] es mayor o menos que la clave, como el vector está ordenado al hacer esto ya podemos descartar una mitad del vector asegurandonos que en esa mitad no está la clave que buscamos. En nuestro caso vector[Icentro] = 4 < 6, entonces la parte del vector vector[0...4] ya puede descartarse. 5. Reasignamos Iarriba o Iabajo para obtener la nueva parte del vector en donde queremos buscar. Iarriba, queda igual ya que sigue siendo el tope. Iabajo lo tenemos subir hasta 5, entonces quedaria Iarriba = 9, Iabajo = 5. Y volvemos al paso 2. Si la clave no fuese encontrada en algun momento Iabajo > Iarriba, con un while vamos a controlar esta condición para salir del ciclo en tal caso y devolver -1 (clave no encontrada). Hagamos modificaciones al código de búsqueda lineal para implementar imp lementar una función de búsqueda binaria. //Búsqueda binaria en un vector.
#include #pragma hdrstop #include #include #include #include int busquedaBinaria(const int[], int, int); //vector, tamaño, clave void ordenarVector(int[], int); //prototipo que modifica y ordena el vector void intercambiar(int&, int&); //prototipo, intercambia los valores de dos elementos void mostrarVector(int[], int); //-------------------------------------------------------------------#pragma argsused int _tmain(int argc, _TCHAR* argv[]) { int clave =0; const int tamano = 15; int vector[tamano] = {20,17,13,16,41,32,12,115,95,84,54,63,78,21,10}; int i; //ordenamos el vector para que funcione la busquedaBinaria cout << "Vector sin ordenar\n" ; mostrarVector(vector,tamano); ordenarVector(vector,tamano); cout << "Elementos del vector ordenado:\n"; mostrarVector(vector,tamano); cout << "Indique un valor a buscar y se le devolvera el indice: " ; cin >> clave; cout<< "Su valor se encuentra en vector["<
EDI – Laboratiorio Programación C++ Prof. Abdala Pablo
30
} int busquedaBinaria(const int vector[], int tamano, int clave) { int Iarriba = tamano-1; int Iabajo = 0; int Icentro; while (Iabajo <= Iarriba) { Icentro = (Iarriba + Iabajo)/2; if (vector[Icentro] == clave) return Icentro; else if (clave < vector[Icentro]) vector[Icentro]) Iarriba=Icentro-1; else Iabajo=Icentro+1; } return -1; } void ordenarVector(int vector[], int tamano) { for (int i = 0; i< tamano -1 ; i++) for (int j = 0; j< tamano -1 ; j++) if (vector[j] > vector[j+1]) intercambiar(vector[j],vector[j+1]); } void intercambiar(int &a, int &b) { int tmp = b; b = a; a = tmp; }
Recursividad Se dice que algo es recursivo si se define en función de sí mismo o a sí mismo. También se dice que nunca se debe incluir la misma palabra en la definición de ésta. El caso es que las definiciones recursivas aparecen con frecuencia en las matemáticas, e incluso en la vida real. Un ejemplo: basta con apuntar una cámara al monitor que muestra la imagen que muestra esa cámara. El efecto es verdaderamente curioso, en especial cuando se mueve la cámara alrededor del monitor. En matemáticas, tenemos múltiples definiciones recursivas: - Números naturales: (1) 1 es número natural. (2) el siguiente número de un número natural es un número natural - El factorial: n!, de un número natural (incluido el 0): (1) si n = 0 entonces: 0! = 1 EDI – Laboratiorio Programación C++ Prof. Abdala Pablo
31
(2) si n > 0 entonces: n! = n · (n-1)! Asimismo, puede definirse un programa en términos recursivos, como una serie de d e pasos básicos, o paso base (también conocido como condición de parada), y un paso recursivo, recursivo, donde vuelve a llamarse al programa. En un computador, esta serie de pasos recursivos debe ser finita, terminando con un paso base. Es decir, a cada paso recursivo se reduce el número de pasos que hay que dar para terminar, llegando un momento en el que no se verifica la condición de paso a la recursividad. Ni el paso base ni el paso recursivo son necesariamente únicos. Por otra parte, la recursividad también puede ser indirecta, si tenemos un procedimiento P que llama a otro Q y éste a su vez llama a P. También en estos casos debe haber una condición de parada. Un ejemplo de programa recursivo en C, el factorial: int factorial(int n) { if (n == 0) return 1; return n * factorial(n-1) ; }
Como se observa, en cada llamada recursiva se reduce el valor de n, llegando el caso en el que n es 0 y no efectúa más llamadas recursivas. Hay que apuntar que el factorial puede obtenerse con facilidad sin necesidad de emplear funciones recursivas, es más, el uso del programa anterior es muy ineficiente, pero es un ejemplo muy claro. A continuación se expone un ejemplo de programa que utiliza recursión indirecta, y nos dice si un número es par o impar. Al igual que el programa anterior, hay otro método mucho más sencillo de determinar si un número es par o impar, basta con determinar el resto de la división entre dos. Por ejemplo: si hacemos par(2) devuelve 1 (cierto). Si hacemos impar(4) devuelve 0 (falso). /* declaracion de funciones, para evitar errores */ int par(int n); int impar(int n); int par(int n) { if (n == 0) return 1; return impar(n-1); } int impar(int n) { if (n == 0) return 0; return par(n-1); }
¿Qué pasa si se hace una llamada recursiva que no termina? Cada llamada recursiva almacena los parámetros que se pasaron al procedimiento, y otras variables necesarias para el correcto funcionamiento del programa. Por tanto si se produce una llamada recursiva infinita, esto es, que no termina nunca, llega un momento en el que no quedará memoria para almacenar más datos, y en ese momento se abortará la ejecución del proEDI – Laboratiorio Programación C++ Prof. Abdala Pablo
32
grama. Para probar esto se puede intentar hacer esta llamada en el programa factorial definido anteriormente: factorial(-1); Por supuesto no hay que pasar parámetros a una función que estén fuera de su dominio, pues el factorial está definido solamente para números naturales, pero es un ejemplo claro.
Fibonacci Sucesión de Fibonacci, en matemáticas, sucesión de números en la que q ue cada término es igual a la suma de los dos términos precedentes: 0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, ... y así sucesivamente, que empieza con 0 y 1. Esta sucesión fue descubierta por el matemático italiano Leonardo Fibonacci. Los números de Fibonacci tienen interesantes propiedades y se utilizan mucho en matemáticas. Las estructuras naturales, como el crecimiento de hojas en espiral en algunos árboles, presentan con frecuencia la forma de la sucesión de Fibonacci. #include #pragma hdrstop #include #include #include #pragma argsused //----------------------------------------------double Fibonacci(int n); int _tmain(int argc, _TCHAR* argv[]) { int n; /* Se solicita al usuario el valor de n */ Cout<<"Ingrese el valor de n: "; Cin>>n; /* Imprime el fibonacci de n */ Cout<<"El termino “<
El triángulo de Pascal En algún momento de tu vida habrás aprendido que (x + z) 2 = x2 + 2xz + z 2, que (x + z)3 = x3 + 3x2z + 3xz2 + z3 y, en general, para cualquier entero positivo n, a calcular los coeficientes de (x + z)n, y posiblemente le diste el nombre de nCm al coeficiente de x mzn-m. Seguramente recuerdas la siguiente tabla triangular: EDI – Laboratiorio Programación C++ Prof. Abdala Pablo
33
1 11 121 1331 14641 ...... Esa tabla se conoce como triángulo de Pascal y se construye como sigue: Al principio se coloca un 1 (que corresponde con 0C0). Para cada renglón subsecuente, digamos para el renglón n, se coloca un 1 a la izquierda y un 1 a la derecha (que corresponden con nC0 y nCn, respectivamente) y los elementos restantes se calculan sumando los dos números que tiene justo arriba a la izquierda y arriba a la derecha, es decir, nCm = n-1Cm-1 + n-1Cm para toda 0 < m < n. Entre otras cosas, el número nCm cuenta la cantidad de formas de escoger m objetos de un total de n objetos distintos. A estos números también se les llama las combinaciones de m ob jetos en n. int comb(int n, int m) { if ((n == 0) || (n == m)) return 1; else return comb(n-1,m-1) + comb(n-1,m); }
Torres de Hanoi La Leyenda Según una leyenda, los monjes del templo de una antigua ciudad de la India tienen que mover una torre de 64 discos sagrados de un sitio a otro. Pero los discos son frágiles así que solo uno de ellos puede moverse a la vez. Ningún disco puede colocarse encima de otro ot ro más pequeño. Y únicamente existe otro lugar en el templo (además del sitio original y el destino) lo suficientemente sagrado para que una torre de discos di scos pueda ponerse ahí.
La leyenda dice además que antes de que los monjes realicen el último movimiento para completar la torre en su nuevo lugar, el templo se reducirá a cenizas y el mundo se acabará.
.
El problema de las Torres de Hanoi EDI – Laboratiorio Programación C++ Prof. Abdala Pablo
34
Este es un problema clásico de recursión, ya que pertenece a la clase de problemas cuya solución se simplifica notablemente al utilizar recursión. Se tienen 3 torres y un conjunto de n discos de diferentes tamaños. Cada uno de ellos tiene una perforación en el centro que les permite deslizarse por cualquiera de las torres. Inicialmente, los n discos están ordenados de mayor a menor en una de las torres (Torre A). Se deben pasar los lo s discos a otra torre, utilizando la del medio como auxiliar. Los movimientos deben hacerse respetando las siguientes reglas: 1. Solo puede moverse un disco a la vez, por lo tanto éste será el que esté en la parte superior de una torre. 2. No se puede colocar un disco grande encima de uno más pequeño. Las torres de identificaran con las letras A, B y C; los discos están inicialmente en la torre A y se desea transferirlos a la torre to rre B, de la misma manera como están en A. Este programa calcula la cantidad de movimientos movimientos necesarios ingresando la cantidad de discos. #include #pragma hdrstop #include #include #include //--------------------------------------------------------------------------int hanoi(int n); #pragma argsused int _tmain(int argc, _TCHAR* argv[]) { int n; int disc, mov; cout<<"::TORRES DE HANOI::\n"; cout<<"Numero cout<<"Numero de discos: "; cin>>disc; cout<<"\tMovimientos necesarios: “<
Operaciones recursivas con Vectores
EDI – Laboratiorio Programación C++ Prof. Abdala Pablo
35
- Dado un vector constituido de números enteros y que contiene N elementos siendo N >= 1, devolver la suma de todos los elementos. int sumarray(int numeros[], int posicion, int N) { if (posicion == N-1) return numeros[posicion]; else return numeros[posicion] + sumarray(numeros, posicion+1, N); } ... int numeros[5] = {2,0,-1,1,3}; int N = 5; cout<
int sumarray(int numeros[], int posicion) { if (numeros[posicion] == -1) return 0; else return numeros[posicion] + sumarray(numeros, posicion+1); } ... int numeros[5] = {2,4,1,-3,-1}; cout<
La razón por la que se incluye este ejemplo se debe a que en general no se conocerá el número de elementos de la estructura de datos sobre la que se trabaja. En ese caso se introduce un centinela -como la constante -1 de este ejemplo o la constante NULO para punteros, u otros valores como el mayor o menor entero que la máquina pueda representar- para indicar el fin de la estructura. - Dado un array constituido de números enteros y que contiene N elementos siendo N >= 1, devolver el elemento mayor. int mayor(int numeros[], int posicion) { int aux; if (posicion == 0) EDI – Laboratiorio Programación C++ Prof. Abdala Pablo
36
return numeros[posicion]; else { aux = mayor(numeros, posicion-1); if (numeros[posicion] > aux) return numeros[posicion]; else return aux; }
} ... int numeros[5] = {2,4,1,-3,-1}; int N = 5; cout<
Archivos en C++ Al igual que ocurre con la escritura en pantalla, a la hora hora de manejar los archivos desde C++, podemos emplear las funciones que ya conocíamos de C, o bien emplear otras nuevas posibilidades que aporta C++. También al igual que ocurría o curría con la pantalla, el manejo de archivos se basará en flujos de entrada y salida. Tenemos las clases fstream (archivo, en general), ifstream (archivo de entrada) y ofstream (archivo de salida), todas ellas definidas en “fstream.h”. Leeremos y escribiremos con << y >>, al igual que para la pantalla. Cerraremos un archivo con “close” (tanto si lo hemos abierto abi erto como para leer o para escribir) y comprobaremos si se ha terminado un archivo de entrada con “eof” (end of file – fin de archivo). Vamos a ver un primer ejemplo que lo aplique, creando un archivo de texto: #include Int main() { ofstream archivo("c: \\ejemplo.txt"); archivo << "Hola" << endl; archivo << "Adios" << endl; archivo.close(); } Debería ser muy fácil de seguir: • Incluimos el archivo de cabecera “fstream.h”. • Definimos un archivo de salida, que tendrá por nombre físico “ejemplo.txt” en la unidad C: \ • Escribimos dos líneas de texto en el archivo. • Cerramos en el archivo. En un caso general, puede ocurrir que no sepamos sepamos el nombre físico del archivo archivo en el momento de definir la variable “archivo”, sino más tarde (por ejemplo, porque el usuario sea el que vaya a teclear el nombre del archivo con el que q ue trabajar, o porque vaya a escoger dicho nombre en una ventana de diálogo). En ese caso, podemos usar la función función miembro “open”.
Como ejemplo, vamos a leer el archivo que acabamos de crear: #include #include EDI – Laboratiorio Programación C++ Prof. Abdala Pablo
37
int main() { fstream archivo; char texto[200];
}
archivo.open("ejemplo.txt", ios::in); // Abro para lectura lectura archivo >> texto; // Leo una primera línea while (!archivo.eof()) // Mientras se haya podido leer algo { cout << texto << endl; // Muestro lo que se lee archivo >> texto; // Y vuelvo a intentar leer } archivo.close(); // Finalmente, cierro
La estructura es ligeramente distinta, pero aun así, debería resultar fácil de seguir. Esta vez, el archivo lo hemos declarado como “genérico”, sin especificar especificar si va a ser para lectura o escritura, de modo que este dato d ato lo indicamos cuando realmente abrimos el archivo. Los modos de apertura que tenemos disponibles son: • ios::in abre el archivo para lectura • ios::out abre el archivo para escritura • ios::append abre el archivo para añadir datos (al final, después de los que ya contenga)
Si hubiéramos declarado el archivo como “ifstream”, se daría por sentado que lo abrimos para leer, y no sería necesario indicarlo: ifstream archivo; // Abro para lectura archivo.open("C: \\ ejemplo.txt"); No hemos comprobado si el archivo archivo realmente se ha podido abrir. Para conseguirlo, añadiríamos después de “open” algo parecido a esto, similar a lo que hacíamos en C estándar:
if (!archivo) { cerr << "No se ha podido abrir el archivo." << endl; exit(1); } Estas son las ideas básicas. Pero hay más posibilidades, que voy a comentar con menos detalle, pero prefiero que se sepa que existen, porque pueden ser útiles en muchos casos. Por ejemplo, podemos leer un bloque de datos de una determinada longitud, lo que será útil cuando manejemos archivos binarios, o escribir una serie de bytes, o leer un dato de un flujo pero sin avanzar de posición. Estas son algunas funciones miembro de iostream que nos servirán para cosas como esas: • put(char c) escribe un carácter en un flujo de salida. • get(char& c) lee un carácter de un flujo de entrada. • read(char* s, int n) lee n bytes del flujo flujo de entrada y los deposita en la cadena cadena s
(normalmente se usará para entrada binaria). • write(const char* s, int n) escribe n bytes de la cadena s en un flujo de salida (normalmente se usará para salida binaria). EDI – Laboratiorio Programación C++ Prof. Abdala Pablo
38
\ n‟) • get(char* s, int n, char c=‟ \ n‟) lee como máximo n caracteres del d el flujo de entrada (incluyendo el „\ 0‟) 0‟) y los introduce en la cadena s, o h asta que encuentre el carácter de terminación (por defecto „ \ n‟, n‟, salto de línea), o el fin de archivo. No retira el carácter de terminación
del flujo de entrada.
\n‟) n‟) lee como máximo n-1 caracteres del flujo de entrada, o • getline(char* s, int n, char c=‟ c=‟ \
hasta que encuentre el carácter de terminación (por defecto un final de (línea) o hasta el fin d e archivo. Retira el carácter de terminación del flujo de entrada, pero no lo almacena en la cadena s. • ignore(int n=1, int delim=EOF) ignora ignora o descarta descarta los n caracteres siguientes de un flujo de entrada (o un solo carácter, si no se indica el valor de n), o hasta que encuentra un cierto carácter de terminación (por defecto el fin de archivo EOF). • peek() lee un carácter carácter del flujo de entrada pero sin retirarlo de dicho flujo. • putback(char c) devuelve devuelve el carácter c al flujo de entrada (de modo que sería lo primero que se leería en la próxima operación de entrada). Por otra parte, en la clase “fstream” tenemos otras funciones miembro que q ue nos ayudarán a
comprobar errores en la lectura o escritura:
• good () devuelve un valor valor distinto de cero si no ha habido habido ningún error. • eof() devuelve un valor distinto de cero si se ha llegado al fin del archivo, como ya hemos
visto.
• bad() devuelve un valor distinto de cero si ha habido un error grave de entrada/salida grave.
No se puede continuar en esas condiciones.
• fail() devuelve un valor valor distinto de cero si ha habido cualquier error error de E/S distinto de
EOF. Después podemos llamar a bad() para comprobar si el error es grave o si se puede intentar proseguir la lectura. Si bad() devuelve 0, el error no es grave y la lectura puede proseguir después de llamar a la función clear(). • clear() resetea la situación situación de error (siempre que no sea grave), grave), para poder seguir leyendo. Además, también podemos comprobar si ha habido algún error en la forma que hemos empleado en el ejemplo anterior: cada función (open, read, etc) devolverá un valor distinto de cero cuando exista algún error, por lo que es habitual emplear construcciones como if (!archivo) { // No se ha podido abrir el archivo } o como if (!archivo.get(ch)) { // No se ha podido leer el siguiente dato. } Ejemplo de lectura de archivo sumo.in, convierte los contenidos en una matriz y guarda los contenidos en un archvivo sumo.out del ejercicio LUCHADORES JAPONESES pag. 103. #include #pragma hdrstop #include #include #include #include #pragma argsused int _tmain(int argc, _TCHAR* argv[]) {
EDI – Laboratiorio Programación C++ Prof. Abdala Pablo
39
fstream archivo; char texto[200]; int matriz[12][2],f,c; archivo.open("c:\\sumo.in", ios::in); // Abro para lectura archivo >> texto; // Leo una primera linea matriz[0][0]=atoi(texto); while (!archivo.eof()) // Mientras se haya podido leer algo { for (f=1; f<=10;f++) { for (c=1; c<=2; c++) { archivo >> texto; // Y vuelvo a intentar leer matriz[f][c]=atoi(texto);// asigno a la matriz los datos leidos } } } ofstream archivoout("c:\\sumo.out"); cout<
// Finalmente, cierro
Backtracking * Introducción * La vuelta del caballo * El problema de las ocho reinas * El problema de la mochila (selección óptima)
Introducción Los algoritmos de vuelta atrás se utilizan para encontrar soluciones a un problema. No siguen unas reglas para la búsqueda de la solución, simplemente una búsqueda sistemática, que más o menos viene a significar que hay que probar todo lo posible hasta encontrar la solución o encontrar que no existe solución al problema. Para conseguir este propósito, se separa la búsqueda en varias búsquedas parciales o subtareas. Asimismo, estas subtareas suelen incluir más subtareas, por lo que el tratamiento general de estos algoritmos es de naturaleza recursiva.
EDI – Laboratiorio Programación C++ Prof. Abdala Pablo
40
¿Por qué se llaman algoritmos de vuelta atrás?. Porque en el caso de no encontrar una solución en una subtarea se retrocede a la subtarea original y se prueba otra cosa distinta (una nueva subtarea distinta a las probadas anteriormente). Puesto que a veces nos interesa conocer múltiples soluciones de un problema, estos algoritmos se pueden modificar fácilmente para obtener una única solución (si existe) o todas las soluciones posibles (si existe más de una) al problema dado. Estos algoritmos se asemejan al recorrido en profundidad dentro de un grafo (ver sección de grafos, estructuras de datos, y recorrido de grafos, algoritmos), siendo cada subtarea un nodo del grafo. El caso es que el grafo no está definido de forma explícita (como lista o matriz de adyacencia), sino de forma implícita, es decir, que se irá creando según avance el recorrido. A menudo dicho grafo es un árbol, o no contiene ciclos, es decir, al buscar una solución es, en general, imposible llegar a una misma solución x solución x partiendo de dos subtareas distintas a y b; o de la subtarea a es imposible llegar a la subtaréa b y viceversa. Gráficamente se puede ver así:
A menudo ocurre que el árbol o grafo que se genera es tan grande que encontrar una solución o encontrar la mejor solución entre varias posibles es computacionalmente muy costoso. En estos casos suelen aplicarse una serie de restricciones, de tal forma que se puedan podar algunas de las ramas, es decir, no recorrer ciertas subtareas. Esto es posible si llegado a un punto se puede demostrar que la solución que se obtendrá a partir de ese punto no será mejor que la mejor solución obtenida hasta el momento. Si se hace correctamente, la poda no impide encontrar la mejor solución. A veces, es imposible impos ible demostrar demos trar que al hacer una poda pod a no se esté ocultando una buena solución. Sin embargo, el problema quizás no pida la mejor solución, sino una que sea razonablemente buena y cuyo coste computacional sea bastante reducido. Esa es una buena razón para aumentar las restricciones a la hora de recorrer un nodo. Tal vez se pierda la mejor solución, pero se encontrará una aceptable en un tiempo reducido. Los algoritmos de vuelta atrás tienen un esquema genérico, según se busque una o todas las soluciones, y puede adaptarse fácilmente según las necesidades de cada problema. A contiEDI – Laboratiorio Programación C++ Prof. Abdala Pablo
41
nuación se exponen estos esquemas, extraídos de Wirth Los bloques se agrupan con begin y end, end, equivalentes a los corchetes de C, además están tabulados. - esquema para una solución: procedimiento ensayar (paso : TipoPaso) repetir | seleccionar_candidato | if aceptable if aceptable then | begin | anotar_candidato | if solucion_incompleta if solucion_incompleta then | begin | ensayar(paso_siguiente) | if no if no acertado then borrar_candidato | end | else begin | anotar_solucion | acertado <- cierto; | end hasta que (acertado = cierto) o (candidatos_agotados) fin procedimiento
- esquema para todas las soluciones: procedimiento ensayar (paso : TipoPaso) para cada candidato hacer | seleccionar candidato | if aceptable if aceptable then | begin | anotar_candidato | if solucion_incompleta if solucion_incompleta then | ensayar(paso_siguiente) | else | almacenar_solucion | borrar_candidato | end hasta que candidatos_agotados fin procedimiento
Por último, se exponen una serie de problemas típicos que se pueden resolver fácilmente con las técnicas de vuelta atrás. El primero que se expone es muy conocido. Se trata de la vuelta del caballo. Muchos problemas de los pasatiempos de los periódicos pueden resolverse con la ayuda de un ordenador y en esta web se s e muestran algunos de ellos.
La vuelta del caballo EDI – Laboratiorio Programación C++ Prof. Abdala Pablo
42
Se dispone de un tablero rectangular, por ejemplo el tablero de ajedrez, y de un caballo, que se mueve según las reglas de este juego. El objetivo es encontrar una manera de recorrer todo el tablero partiendo de una casilla determinada, de tal forma que el caballo pase una sola vez por cada casilla. Una variante es obligar al caballo a volver a la posición de partida en el último movimiento. Por último se estudiará como encontrar todas las soluciones posibles partiendo de una misma casilla. Para resolver el problema hay que realizar todos los movimientos posibles hasta que ya no se pueda avanzar, en cuyo caso hay que dar marcha atrás, o bien hasta que se cubra el tablero. Además, es necesario determinar la organización de los datos para implementar el algoritmo. - ¿Cómo se mueve un caballo?. Para aquellos que no sepan jugar al ajedrez se muestra un gráfico con los ocho movimientos que puede realizar. Estos movimientos serán los ocho candidatos.
Con las coordenadas en las que se encuentre el caballo y las ocho coordenadas relativas se determina el siguiente movimiento. Las coordenas relativas se guardan en dos arrays: ejex = [2, 1, -1, -2, -2, -1, 1, 2] ejey = [1, 2, 2, 1, -1, -2, -2, -1] El tablero, del tamaño que sea, se representará mediante un array bidimensional de números enteros. A continuación se muestra un gráfico con un tablero de tamaño 5x5 con todo el recorrido partiendo de la esquina superior izquierda.
EDI – Laboratiorio Programación C++ Prof. Abdala Pablo
43
Cuando se encuentra una solución, una variable que se pasa por referencia es puesta a 1 (cierto). Puede hacerse una variable de alcance global y simplificar un poco el código, pero esto no siempre es recomendable. Para codificar el programa, es necesario considerar algunos aspectos más, entre otras cosas no salirse de los límites del tablero y no pisar una casilla ya cubierta (selección del candidato). Se determina que hay solución cuando ya no hay más casillas que recorrer. A continuación se expone un código completo en C, que recubre un tablero cuadrado de lado N partiendo de la posición (0,0). #include #define N 5 #define ncuad N*N void mover(int tablero[][N], int i, int pos_x, int pos_y, int *q); const int ejex[8] = { -1,-2,-2,-1, 1, 2, 2, 1 }, ejey[8] = { -2,-1, 1, 2, 2, 1,-1,-2 }; int main(void) { int tablero[N][N]; /* tablero del caballo. */ int i,j,q; /* inicializa el tablero a cero */ for (i = 0; i < N; i++) for (j = 0; j < N; j++) tablero[i][j] = 0; /* pone el primer movimiento */ tablero[0][0] = 1; mover(tablero,2,0,0,&q); if (q) { /* hay solucion: la muestra. */ for (i = 0; i < N; i++) { for (j = 0; j < N; j++) printf("%3d ", tablero[i][j]); putchar('\n'); } } else printf("\nNo existe solucion\n"); }
return 0;
void mover(int tablero[][N],int i, int pos_x, int pos_y, int *q) { int k, u, v; k = 0; *q = 0; EDI – Laboratiorio Programación C++ Prof. Abdala Pablo
44
}
do { u = pos_x + ejex[k]; v = pos_y + ejey[k]; /* seleccionar candidato */ if (u >= 0 && u < N && v >= 0 && v < N) { /* esta dentro de los limites? */ if (tablero[u][v] == 0) { /* es valido? */ tablero[u][v] = i; /* anota anota el candidato candidato */ if (i < ncuad) ncuad) { /* llega al final del recorrido? */ mover(tablero,i+1,u,v,q); if (!*q) tablero[u][v] = 0; /* borra el candidato */ } else *q = 1; /* hay solucion */ } } k++; } while (!*q && k < 8);
Cambiando el valor de N puede obtenerse una solución para un tablero cuadrado de tamaño N. A continuación, se muestra una adaptación del procedimiento que muestra todas las soluciones. Si se ejecuta para N = 5 se encuentra que hay 304 soluciones partiendo de la esquina superior izquierda. Cuando se encuentra una solución se llama a un procedimiento (no se ha codificado aquí) que imprime todo el tablero. void mover(int tablero[][N],int i, int pos_x, int pos_y) { int k, u, v;
}
for (k = 0; k < 8; k++) { u = pos_x + ejex[k]; v = pos_y + ejey[k]; if (u >= 0 && u < N && v >= 0 && v < N) { /* esta dentro de los limites */ if (tablero[u][v] == 0) { tablero[u][v] = i; if (i < ncuad) mover(tablero,i+1,u,v); else imprimir_solucion(tablero); tablero[u][v] = 0; } } }
El problema de las ocho reinas Continuamos con problemas relacionados con el ajedrez. El problema que ahora se plantea es claramente, como se verá, de vuelta atrás. Se recomienda intentar resolverlo a mano. Se trata de colocar ocho reinas sobre sobr e un tablero de ajedrez, de tal forma que ninguna amenace (pueda comerse) a otra. Para los que no sepan ajedrez deben saber que una reina amenaza a otra pieza que esté en la misma columna, fila o cualquiera de las cuatro diagonales. La dificultad que plantea este problema es la representación de los datos. Se puede utilizar un array bidimensional de tamaño 8x8, pero las operaciones para encontrar una reina que amenaEDI – Laboratiorio Programación C++ Prof. Abdala Pablo
45
ce a otra son algo engorrosas y hay un truco para evitarlas. La solución aquí expuesta vuelve a ser tomada de Wirth Es lógico que cada reina debe ir en una fila distinta. Por tanto, en un array se guarda la posición de cada reina en la columna que se encuentre. Ejemplo: si en la tercera fila hay una reina situada en la quinta columna, entonces la tercera posición del array guardará un 5. A este array se le llamará col. Hace falta otro array que determine si hay puesta una reina en la fila j-ésima. A este array se le llamará fila. fila. Por último se utilizan dos arrays más para determinar las diagonales libres, y se llamarán diagb y diagc. Para poner una reina se utiliza esta instrucción: col[i] = j ; fila[j] = diagb[i+j] = diagc[7+i-j] = FALSE; Para quitar una reina esta otra: fila[j] = diagb[i+j] = diagc[7+i-j] = TRUE; Se considera válida la posición para este caso: if (fila[j] && diagb[i+j] && diagc[7+i-j]) d iagc[7+i-j]) entonces proceder ... A continuación se expone el código completo en C. Se han utilizado tipos enumerados enumerados para representar los valores booleanos. #include enum bool {FALSE, TRUE}; typedef enum bool boolean; void ensayar(int i, boolean *q, int col[], boolean fila[], boolean bo olean diagb[], boolean diagc[]); int main(void) { int i; boolean q; int col[8]; boolean fila[8],diagb[15], diagc[15]; for (i = 0; i < 8; i++) fila[i] = TRUE; for (i = 0; i < 15; i++) diagb[i] = diagc[i] = TRUE; ensayar(0,&q,col,fila,diagb,diagc); if (q) { printf("\nSolucion:"); for (i = 0; i < 8; i++) printf(" %d", col[i]); } else printf("\nNo hay solucion"); }
return 0; EDI – Laboratiorio Programación C++ Prof. Abdala Pablo
46
void ensayar(int i, boolean *q, int col[], boolean fila[], boolean bo olean diagb[], boolean diagc[]) { int j; j = 0; *q = FALSE; do { if (fila[j] && diagb[i+j] && diagc[7+i-j]) { col[i] = j; fila[j] = diagb[i+j] = diagc[7+i-j] = FALSE; if (i < 7) { /* encuentra solucion? */ ensayar(i+1,q,col,fila,diagb,diagc); if (!*q) fila[j] = diagb[i+j] = diagc[7+i-j] = TRUE; } else *q = TRUE; /* encuentra la solucion */ } j++; } while (!*q && j < 8); } Por último, se deja al lector que implemente un procedimiento que encuentre todas las soluciones. Si se desea complicar más entonces se puede pedir que encuentre todas las soluciones distintas, es decir, aquellas que no sean s ean rotaciones o inversiones de otras soluciones. Ahora que se conoce el método general, puede hacerse extensible a múltiples piezas simultáneamente.
El Problema de la mochila (selección óptima) Con anterioridad se ha estudiado la posibilidad de encontrar una única solución a un problema y la posibilidad de encontrarlas todas. Pues bien, ahora se trata de encontrar la mejor solución, la solución óptima, de entre todas las soluciones.
Partiendo del esquema que genera todas las soluciones expuesto anteriormente se puede obtener la mejor solución (la solución óptima, seleccionada entre todas las soluciones) si se modifica la instrucción almacenar_solucion por esta otra: si f(solucion) > f(optimo) entonces optimo <- solución
siendo f(s) función positiva, optimo es la mejor solucion encontrada hasta el momento, y solu- cion es una solucion que se está probando.
El problema de la mochila consiste en llenar una mochila con una serie de objetos que tienen una serie de pesos con un valor asociado. Es decir, se dispone de n tipos de objetos y que no EDI – Laboratiorio Programación C++ Prof. Abdala Pablo
47
hay un número limitado de cada tipo de objeto (si fuera limitado no cambia mucho el problema). Cada tipo i de objeto tiene un peso w i i positivo y un valor v i i positivo asociados. La mochila tiene una capacidad de peso igual a W . Se trata de llenar la mochila de tal manera que se ma ximice el valor de los objetos incluidos pero respetando al mismo tiempo la restricción de capacidad. Notar que no es obligatorio que una solución óptima llegue al límite de capacidad de la mochila.
Ejemplo: se supondrá: n=4 W=8 w() = 2, 3, 4, 5 v() = 3, 5, 6, 10 Es decir, hay 4 tipos de objetos obj etos y la mochila tiene una capacidad de 8. Los pesos varían entre 2 y 5, y los valores relacionados varían entre 3 y 10. Una solución no óptima de valor 12 se obtiene o btiene introduciendo cuatro objetos de peso 2, o 2 de peso 4. Otra solución no óptima de valor 13 se obtiene introduciendo 2 objetos de peso 3 y 1 objeto de peso 2. ¿Cuál es la solución óptima?. A continuación se muestra una solución al problema, problema, variante del esquema para obtener todas las soluciones. void mochila(int i, int r, int solucion, int *optimo) { int k;
}
for (k = i; k < n; k++) { if (peso[k] <= r) { mochila(k, r - peso[k], solucion + valor[k], optimo); if (solucion + valor[k] > *optimo) *optimo = solucion+valor[k]; } }
Dicho procedimiento puede ser ejecutado de esta manera, siendo n, W, peso y valor variables globales para simplificar el programa: n = 4, W = 8, peso[] = {2,3,4,5}, valor[] = {3,5,6,10}, optimo = 0; ... mochila(0, W, 0, &optimo);
Ejemplo Backtraking
Caminos
EDI – Laboratiorio Programación C++ Prof. Abdala Pablo
48
Sea un tablero de dimensiones MxN, 1<=M<=9, 1<=N<=9, tal que cada casilla contenga una letra mayúscula. La casilla que está en la fila m y la columna n la identificamos mediante (m,n). Dos casillas diferentes (mi,ni) y (mj,nj) son so n adyacentes si se cumple: - para la primera componente, |mi-mj|<=1 o |mi-mj|=M-1, |m i-mj|=M-1, y - para la segunda componente, |ni-nj|<=1 o |ni-nj|=N-1. Es decir, son adyacentes todas aquellas casillas que rodean a una dada, considerando que en el tablero como si la última fila estuviera unida a la primera, y lo mismo para las columnas. En el dibujo siguiente marcamos con un asterisco las casillas adyacentes a las casillas (2,3) (a la izquierda) y (1,1) (a la derecha) en un tablero 4x4: .*** .*.* .*.* **.* .*** .... .... **.* Dada una palabra de k letras mayúsculas A=a1 a2 ... ak, k>=1, decimos que A está contenida en el tablero si se cumple que: - existe una casilla (m1,n1) que contiene la letra a1, - para cada letra ai+1, 1<=i
49
- Línea 1: valores de M y N (un carácter del '1' al '9') separados por un único blanco - Líneas de la 2 a la M+1 (la línea k representa la fila k-1 del tablero): N caracteres, representando el contenido de la línea correspondiente del tablero - Línea M+2: p caracteres, M*N>=p>=1, que representa la palabra a tratar. Salida A guardar en el archivo de caracteres caracteres "CAMI.OUT": p líneas (una para cada letra de la palabra a tratar), siendo el contenido de la línea k igual a la casilla que aparece en posición k dentro del camino de la palabra, de esta forma: carácter del '1' al '9' - blanco - carácter del '1' al '9' Ejemplo de entrada 44 SHAZ IOLG EZEF OHDI SOLA Ejemplo de salida 11 22 23 13 Este problema se ha resuelto utilizando la recursividad (marcha atrás, backtracking). Primero se busca la primera letra de la palabra buscada, y una vez encontrada se busca la siguiente entre las adyacentes. Así se miran todas las posibilidades hasta que se encuentra la palabra buscada. Es un método de fuerza bruta, un poco lento para casos grandes, pero muy efectivo cuando el tamaño de la tabla es pequeño (el límite en este problema es 9). El problema en sí no tiene más dificultades, pero se pueden utilizar un par de trucos para hacer el código más sencillo: 1.- No está permitido recorrer dos veces la misma casilla en una palabra, por lo que hay que tener algo que nos indique si una casilla se ha utilizado anteriormente o no. Para eso se utiliza otra tabla auxiliar del mismo tamaño que la original, inicializada a 0. Cuando una casilla se utiliza, la casilla correspondiente de la tabla auxiliar pasa a marcar 1, así cuando se vaya a utilizar una casilla primero habrá que mirar si ya se ha usado o no (marca 1 ó 0). Hay que tener cuidado, al hacer backtracking hay que volver a poner la casilla a 0. 2.- Para mirar las casillas adyacentes en busca de una nueva letra se puede implementar una a una las 8 direcciones, lo que haría un código largo, engorroso y difícil de depurar, o se puede tener en dos arrays los incrementos posibles de sila y de columna; si estamos en (F,C), podemos ir a (F-1,C-1), (F-1,C), (F-1,C+1), (F,C-1), (F,C+1), (F+1,C-1), (F+1,C) y (F+1,C+1). Si tenemos un array con los valores: EDI – Laboratiorio Programación C++ Prof. Abdala Pablo
50
FF={-1,-1,-1, 0, 0, 1, 1, 1}; CC={-1, 0, 1,-1, 1,-1, 0, 1}; se puede llegar a las 8 casillas con un bucle for, del tipo: for(i=0;i<8;i++) { filanueva = filaantigua + FF[i]; columnanueva = columnaantigua + CC[i]; ... } Así se ahorra líneas de código, y en caso de haber errores, con modificarlo en un sitio basta. 3.- El problema también considera adyacentes de una casilla situada en un borde las casillas del lado opuesto, como si la tabla fuera cíclica. Para no tener que preocuparse de este problema, al hallar las casillas adyacentes basta con hacerlas módulo número de filas (o columnas, según corresponda): filanueva = (filaantigua + FF[i]) % numerodefilas; columnanueva = (columnaantigua + CC[i]) % numerodecolumnas; Pero esto falla en un caso, cuando filaantigua es 0, y FF[i] es -1, filanueva toma el valor -1 (lo que daría un runtime error). Para evitar eso, y aprovechándonos de que a % b = (a + b) % b, basta con poner: filanueva = (filaantigua + FF[i] + numerodefilas) % numerodefilas; columnanueva = (columnaantigua + CC[i] + numerodecolumnas) % numerodecolumnas; Así nos aseguramos de que las nuevas coordenadas son positivas. 4.- El programa te pide que des las posiciones de las casillas en las que está la palabra, ordenadas del principio al final, y al hacer backtracking salen ordenadas justo al revés (del final al principio). Para dar la salida correctamente hay dos soluciones: una es guardar las casillas en una tabla, lo que supone un gasto de memoria (y de tiempo, importante en una función recursiva). Lo más eficaz es buscar la palabra del final al principio, en vez de buscarla del principio al final. Por ejemplo, si te piden hallar la palabra SOLA, hallamos la palabra ALOS, y al dar las coordenadas de esta palabra al revés, se obtienen las coordenadas de la palabra SOLA en orden correcto. #include #pragma hdrstop #include #include #include #pragma argsused bool busca(int,int,int); // Función recursiva que // busca la palabra pedida en la tabla. char mapa[9][9]; // Variable que contiene la tabla. char v[9][9]; // Indica si hemos utilizado una casilla o no.
EDI – Laboratiorio Programación C++ Prof. Abdala Pablo
51
int F,C; // Número de filas y columnas de la tabla. char cad[100]; int l; // Palabra a buscar y su longitud. int ff[8]={1,1,1,-1,-1,-1,0,0}; // Incremento de la fila int cc[8]={-1,0,1,-1,0,1,-1,1}; // Incremento de la columna int main(int argc, char* argv[]) { int a,b; ifstream archivo_entrada("c:\\cami.dat"); ofstream archivo_salida("c:\\cami.out");
// Abro los archivos.
archivo_entrada>>F; archivo_entrada>>C; // Leo el número de filas y columnas. for(a=0;a>mapa[a][b]; } archivo_entrada>>cad; // Leo la palabra a buscar l=strlen(cad); // y su longitud. memset(v,0,sizeof(v)); // No he utilizado ninguna casilla. for(a=0;a
EDI – Laboratiorio Programación C++ Prof. Abdala Pablo
52
} v[f1][c1]=0; // Se marca como no utilizada } } return(false); // No se ha encontrado la palabra. }
Cadenas de caracteres en C++. STRING S TRING Una cadena de caracteres en C++ no es más que un vector de caracteres. #include #include using namespace std; int main(int argc, char* argv[]) {
// inicializaciones string sCadena; string sCadena2("hola2"); sCadena = "hola"; cout << "1: " << sCadena << " " << sCadena2 << endl;
// paso de char* a string y viceversa char szCadena[10]="adios"; sCadena = szCadena; cout << "2: " << sCadena << endl; sCadena = "hola"; strcpy(szCadena,sCadena.c_str()); cout << "2b: " << szCadena << endl;
// operaciones de acceso cout << "3: " << "[3] " << sCadena[3] << endl; cout << "4: " << sCadena.substr(0,3) << endl;
// operaciones de busqueda cout << "5: " << sCadena.find("la",0) << endl;
// operaciones de modificacion cout << "6: " << sCadena.erase(0,2) << endl;
// operación de concatenación cout << "7: " << sCadena + sCadena2 << endl;
// operación de comparación cout << "8: " << (sCadena == sCadena) << endl;
// Operacion de longitud cout << "9: " << sCadena.length() << endl;
// Operacion de tamaño cout << "10: " << sCadena.size() << endl; return 0;
operador[]
EDI – Laboratiorio Programación C++ Prof. Abdala Pablo
53
Imprime el contenido de la variable str, caracter por caracter con el ciclo for. #include #include using namespace using namespace std; int main ()
{ string str ("prueba de string"); int i; for (i=0; i < str.length(); i++) { cout << str[i]; } return 0; }
Swap Vuelca el contenido de un String en otro
#include #include using namespace std; int main(int argc, char* argv[]) { string buyer ("dinero"); string seller ("mercaderia"); cout << "Antes de cambiar el comprador tiene " << buyer; cout << " y el vendedor " << seller << endl; seller.swap (buyer); cout << " Antes de cambiar el comprador tiene " << buyer; cout << " y el vendedor tiene " << seller << endl; system(“pause”); return 0; }
find_first_of Encuentra caracteres en una variable String #include #pragma hdrstop #pragma argsused #include #include using namespace std; EDI – Laboratiorio Programación C++ Prof. Abdala Pablo
54
int main(int argc, char* argv[]) { string str ("Remplaza las vocales por asteriscos."); size_t found; found=str.find_first_of("aeiou"); while (found!=string::npos) { str[found]='*'; found=str.find_first_of("aeiou",found+1); } cout << str << endl; system(“pause”);
return 0; }
Biblioteca conio Contiene los prototipos de las funciones, macros, y constantes para preparar y manipular la consola en modo texto en el entorno de d e MS-DOS. Nombre Función clrscr
getch
getchar
Descripción Esta función despeja la ventana de texto actual y coloca el cursor en la esquina superior izquierda: posición (1,1). La función getch retorna el carácter leído desde el teclado. Lee un caracter de el teclado y regresa el caracter leido.
gotoxy(int x, int y);
Mueve el cursor de la ventana de texto a la posición según las coordenadas especificadas por los argumentos x argumentos x e y. y. Si las coordenadas no son válidas entonces la llamda a la función gotoxy es ignorada. Los argumentos no pueden ser 0.
Ejemplo clrscr(); system(“pause”);
char vocal; vocal=getchar(); gotoxy( 1, 15 );
Librería math.h es un archivo de cabecera de la biblioteca estándar . Muchas de sus funciones incluyen el uso de números en coma flotante. Nombre acos asin atan atan2 cos
Descripción arcocoseno arcoseno arcotangente arcotangente de dos parámetros coseno
EDI – Laboratiorio Programación C++ Prof. Abdala Pablo
55
abs fmod
valor absoluto resto del punto flotante printf ("fmod de 5.3 / 2 es%lf\n", fmod (5.3,2) );
pow(x,y) eleva un valor dado a un exponente, x y sin seno Sqr Cuadrado de un número
int main () { double param, result; param = 1024.0; result = sqrt (param); printf ("sqrt(%lf) = %lf\n", param, result ); return 0; }
sqrt tan
raíz cuadrada tangente
Punteros Introducción El puntero es una técnica muy potente que hace que la programación C++ sea tan utilizada. La técnica de punteros apoya a la programación orientada a objetos, que es una de las grandes diferencias entre C y C++. Definición: Un puntero es una variable que almacena una dirección de memoria.
Operador de Dirección: &
EDI – Laboratiorio Programación C++ Prof. Abdala Pablo
56
Referencias
EDI – Laboratiorio Programación C++ Prof. Abdala Pablo
57
EDI – Laboratiorio Programación C++ Prof. Abdala Pablo
58
EDI – Laboratiorio Programación C++ Prof. Abdala Pablo
59
EDI – Laboratiorio Programación C++ Prof. Abdala Pablo
60
EDI – Laboratiorio Programación C++ Prof. Abdala Pablo
61
EDI – Laboratiorio Programación C++ Prof. Abdala Pablo
62
EDI – Laboratiorio Programación C++ Prof. Abdala Pablo
63
EDI – Laboratiorio Programación C++ Prof. Abdala Pablo
64
EDI – Laboratiorio Programación C++ Prof. Abdala Pablo
65
EDI – Laboratiorio Programación C++ Prof. Abdala Pablo
66
EDI – Laboratiorio Programación C++ Prof. Abdala Pablo
67
EDI – Laboratiorio Programación C++ Prof. Abdala Pablo
68
EDI – Laboratiorio Programación C++ Prof. Abdala Pablo
69
Templates INTRODUCCIÓN Las técnicas técnicas de Templates Templates en C++, permiten un grado de programación genérica, códigos especiales para problemas especializados.
creando
Por ejemplo, la idea del valor valor mínimo o máximo, se repite infinidad de veces veces en la programación, aunque los objetos a evaluar varíen de un caso a otro por el tipo de datos. Sobre esta idea surgió un nuevo paradigma denominado programación genérica o funcional. La programación genérica está mucho más centrada en los algoritmos que en los datos y su postulado fundamental puede sintetizarse en una palabra: generalización. Significa que, en la EDI – Laboratiorio Programación C++ Prof. Abdala Pablo
70
medida de lo posible, los los algoritmos deben ser parametrizados parametrizados al máximo y expresados de la forma más independiente posible de detalles concretos, permitiendo así que puedan servir para la mayor variedad posible de tipos y estructuras de datos. A menudo se tiene el pensamiento que un programa es la implementación de un mapeo (mapping). El programa toma valores y mapea entonces a su salida. En la programación imperativa este mapping es realizado en forma indirecta, por los comandos que leen valores de entrada, manipulan estos y luego escriben las salidas. Los comandos influyen en los programas mediante el uso de las variables almacenadas en la memoria. En la programación funcional, el mapping de valores de entrada a valores de salida es realizado de modo directo. El programa es una función o grupo de funciones; sus relaciones son muy simples: entre ellas se invocan. Los programas son escritos en un lenguaje de expresiones, funciones y declaraciones. El código es similar siempre, pero estamos obligados a rescribir ciertas funciones que dependen del tipo o de la clase del objeto que se almacena. Tómese por ejemplo el problema de encontrar el valor mayor de un par de datos; tanto para un entero, flotante, carácter, etc. Se hace uso de las funciones prototipos; en este caso para encontrar el mayor de dos valores para distintos tipos de datos: Se hace uso de esta sobrecarga de funciones, funciones, para obtener el mayor entre dos enteros, doble flotantes y carácter. int max(int,int); double max(double,double); char max(char,char);
#include #include // sobrecarga de funciones para max() int max(int,int); double max(double,double); char max(char,char); void main() { int a=5,b=3; cout <<" El mayor de los enteros es : “ << max(a,b)<< endl; double c=5.5,d=10; cout << “ El mayor de los flotantes es: “ << max(c,d)<< endl; char e='a',f='A'; cout << “ El mayor de los char es : “ << max(e,f)<< endl; system(“PAUSE”); system(“PAUSE”); } int max(int a,int b) { return a>b?a:b; } EDI – Laboratiorio Programación C++ Prof. Abdala Pablo
71
double max(double a,double b) { return a>b?a:b; } char max(char a,char b) { return a>b?a:b; }
Un Template es una forma de objeto, que se aplica a diferentes instancias, sin especificar el tipo de objeto a ser referenciado. El patrón con un simple código cubre un gran rango de funciones de sobrecarga denominadas funciones patrones o un gran rango de clases denominadas clases patrones. Se dice que los patrones son como una una factoría de ensamblaje, ensamblaje, porque producen una variedad de objetos; es decir, dado un material se produce un determinado artículo. Las templates permiten parametrizar estas clases para adaptarlas a cualquier tipo. Así el cambio de las tres sobrecargas, sob recargas, viene reemplazado siguiente Template: Esta función trabaja para enteros, como también para flotantes y para caracteres. Afortunadamente existen los template que hacen la la labor de programación más fácil. Los templates también denominadas tipos parametrizados, son un mecanismo de C++ que permite que un tipo pueda ser utilizado como parámetro en la definición de una clase o una función. template T max(T a,T b) { return a>b?a:b; }
#include #pragma hdrstop #include #include // sobrecarga de funciones para max() template T max(T a,T b) { return a>b?a:b; } #pragma argsused int main(int argc, char* argv[]) { int a=5,b=3; cout << " El mayor de los enteros es : " << max(a,b)<< endl; double c=5.5,d=10; cout << " El mayor de los flotantes es: " << max(c,d)<< endl; char e='a',f='A'; EDI – Laboratiorio Programación C++ Prof. Abdala Pablo
72
cout << " El mayor de los char es : " << max(e,f)<< endl; system("PAUSE"); return 0; }
C++ permite crear Templates de funciones y de clases. La sintaxis para declarar declarar un template de función es parecida a la de cualquier otra función, pero se añade al principio una presentación de la clase que se usará como referencia en la plantilla: La lista de clases que se incluye a continuación continuación de la palabra reservada template se escribe entre las llaves "<" y ">"; y en este caso caso esos símbolos no indican que se debe introducir un literal. Se ha considerado que el template, radique en un archivo tipo header, donde se ha incluido la función plantilla min(), template[...]> typename[...]> () { // cuerpo de la función }
Considerar que el template radique en un archivo tipo header, donde se ha incluido incluido la función función plantilla min(). // minymax.h #ifndef __MINYMAX_H #define __MINYMAX_H template T max(T a,T b) { return (a>b)?a:b;} template T min(T a,T b) { return (a #include #include "minymax.h" void main() // Eduardo Raffo Lecca { int x1=4,x2=3; double y1=7.5,y2=8.3; char c1='a',c2='A'; c1='a',c2='A'; EDI – Laboratiorio Programación C++ Prof. Abdala Pablo
73
cout << "min int : " << min(x1,x2) << endl; cout << "max double : " << max(y1,y2) << endl; cout << "max char : " << max(c1,c2) max(c1,c2) << endl; system("PAUSE"); } Un Template que que ejecuta el valor máximo para tres valores #include #include template T maximo(T a,T b,T c) { T max=(a>b?a:b); return(max>c?max:c); } Int main() { int a,b,c; cout << "ingrese 3 enteros cin >> a >> b >> c; int j=maximo(a,b,c); cout << "mayor : " << j << char d,e,f; cout << "ingrese 3 char cin >> d >> e >> f; char k=maximo(d,e,f); cout << "mayor : " << k << system("PAUSE"); }
: " << endl;
endl; : " << endl;
endl;
Programación visual Introducción y entorno de trabajo.
Comenzaremos a programar en forma visual, para ello utilizaremos un compilador, el Borland C++ Builder. Este compilador respeta las mismas teclas rápidas que los compiladores que veníamos usando en C++ (dado que todos son del mismo fabricante, Borland). Este compilador genera aplicaciones para entornos visuales (en esta versión Windows 95 o superior. Pensemos en una ventana clásica de Windows. Dentro de la venta tendremos botones, cuadros de edición, etiquetas, barras de desplazamiento, un menú principal, etc Hay tres aspectos importantes que nos interesaran sobre los l os objetos:
EDI – Laboratiorio Programación C++ Prof. Abdala Pablo
74
Propiedades Eventos Métodos
1) Propiedades: Como su nombre lo indica, son las características de los objetos (Tamaño, posición etc.). Si el objeto fuese una estructura, las propiedades serian los campos de la estructura. De hecho, utilizaremos las propiedades como campos de una estructura, ya que veremos que tienen tipos (Int, Char, String, etc.)
2) Eventos: Un evento es un mensaje que el objeto envía al programa de que "algo" a sucedido. Si hacemos click sobre un objeto, eso es un evento, si cambiamos el tamaño de un objeto obj eto (por ejemplo la ventana) es un evento si modificamos el texto de un cuadro de edición hay un evento, si tocamos una tecla, hay un evento. De ahora en adelante, en lugar de pensar el programa como un solo bloque, lo pensaremos como pequeños bloques de programa que se ejecutaran cuando suceda un determinado evento. Por ejemplo, si quiero que al tocar un botón aparezca un mensaje, en el evento OnClick del botón escribo una línea de código que q ue diga: ShowMessage("Hola, que tal"); tal"); (Evidentemente ShowMessage muestra un un mensaje, pero esto lo veremos en después con mayor detalle).
3) Métodos: Los Objetos son de una determinada clase. Podríamos decir que una clase (class) es una estructura mejorada. Mas exactamente, es una estructura que además de tener campos, tiene funciones relacionadas a ella. Estas funciones se llaman Métodos. A veces es común confundir las propiedades con los métodos, sin embargo son cosas totalmente diferentes. Una propiedad es una cualidad que esta expresada con algún tipo de dato (enteros, caracteres, etc.). Un método en cambio es una acción que puede realizar el ob jeto. Por ejemplo la ventana tiene el método Close() que obviamente cierra la ventana. ventana.
Entonces: Las propiedades son datos a los cuales podemos acceder en tiempo de ejecución y (algunas) en tiempo de edición. Por ejemplo el ancho de una ventana lo definimos cuando estamos editando nuestra aplicación, pero lo podemos cambiar en tiempo de ejecución. Los eventos son sucesos. Nosotros programaremos los bloques de programación (también llamados cápsulas) dentro de los eventos que nos interesen. Los métodos son acciones que nosotros invocamos en tiempo de ejecución.
EDI – Laboratiorio Programación C++ Prof. Abdala Pablo
75
Debemos tomar algunos recaudos antes de comenzar a programar. Pensemos en un programa de Windows, muchas veces los programas tiene mas de una ventana (ventanas de configuración, cuadros de dialogo, etc.) A cada Ventana le corresponderían dos archivos (un *.CPP y un *.H) a su vez, existe un archivo que reúne a todas las ventanas en un mismo proyecto (en nuestra versión es un archivo *.MAK, en versiones posteriores, *.BPR). Además de estos archivos, el compilador creara nuevos archivos de recursos, archivos temporales, etc.
Importante: Nuestro programa (de ahora en adelante será un proyecto) esta formados por muchos archivos. Es por eso que antes de escribir un sola línea de código, lo que haremos será guardar el proyecto en una carpeta nueva a la que le pondremos un nombre conveniente, recién entonces comenzaremos a escribir nuestro programa (acordate, siempre grabaremos y abriremos proyectos). Cuando queramos salvar nuestro trabajo utilizaremos la opción save all que guardará todos los archivos de nuestro proyecto. Si tomamos esta medida de seguridad nos ahorraremos problemas. Cabe aclarar que el compilador genera algunos archivo temporales que agilizan el proceso de compilación pero que son grandes (varios mega) por lo que es muy incomodo de trasladarlos en disquetes. Estos archivos no son necesarios, y podemos borrarlos pues el compilador los generara automáticamente en la próxima compilación, los archivos que debemos guardar (y los que debemos a copiar a un disquete en caso de querer trasladar un proyecto de un lado a otro) son:
*.MAK
*.CPP
*.RES
*.H
*.HPP
*.RC
*.DEF
*.DFM
Características del compilador: Al abrir el compilador nos encontramos (en principio) con tres ventanas: Estas ventanas son:
EDI – Laboratiorio Programación C++ Prof. Abdala Pablo
76
Barra de herramientas
Inspector de objetos
Esquema de la ventana
Barra de Herramientas: Aquí encontramos las opciones del compilador, (Opciones de archivos, de edición, configuración, etc.), así como también lo controles de compilación (ejecutar, compilar, detener etc.), y lo mas novedoso para nosotros, la barra de componentes. La barra de componentes es una pagina de pestañas en donde están clasificados los distintos componentes (objetos) que podremos colocar en nuestra aplicación. Por ejemplo, si queremos poner un botón, seleccionamos de la pestaña Standar el objeto con forma de botón (que dice Ok) y lo arrastramos a la Ventana Esquema en el lugar de la ventana que deseamos colocarlo (como si se tratase del PaintBrush). EDI – Laboratiorio Programación C++ Prof. Abdala Pablo
77
El esquema de la ventana: Es el aspecto (aproximado) que tendrá la ventana de nuestro proyecto al comenzar su ejecución (si nuestro proyecto tuviese mas de una ventana habrá más de un esquema, por supuesto podremos cerrar las ventanas que no estemos modificando). Este aspecto podría cambiar en tiempo de ejecución por ejemplo por redimensionamientos de la ventana. Los puntos que vemos dibujados tienen la función de grilla (o imán) para facilitar la colocación de los objetos, esta grilla puede desactivarse, o modificarse, pero en general nos ayuda muchísimo. De todo modos esos puntos no se verán en la aplicación terminada. Es en esta ventana donde colocaremos los objetos desde la barra de componentes. Si ocultáramos accidentalmente esta ventana, reaparecerá al pulsar F10 .
El Inspector de Objetos: Es una ventana con dos pestañas, Properties (Propiedades) y Events (eventos). En estas pestañas figuran las propiedades y los eventos de los objetos que tenemos en nuestra aplicación. Es importante que sepas que los valores que le asignamos a las propiedades los podremos modificar luego en tiempo de ejecución. Si ocultáramos accidentalmente esta ventana, reaparecerá al pulsar F11.
En un principio puede resultarte incomodo el hecho de que estas ventanas estén "flotando" en el escritorio, ya que estamos acostumbrados a programas que tienen una ventana madre y que contiene todas las ventanas de la aplicación (Word, Excel, etc.) Sin embargo no tardaras en acostumbrarte a este entorno de trabajo. Antes de terminar con este capitulo debemos tener en cuenta algo: Cuando programábamos en C a modo consola, consola, estábamos dentro de una aplicación aplicación (el compilador). Al probar nuestro programa el compilador quedaba en segundo plano mientras nuestro programa se ejecutaba, y al terminar volvíamos automáticamente al compilador. Ahora en cambio nos encontramos en un entorno Windows, que es multitarea, es decir, puede ejecutar varias aplicaciones al mismo tiempo (si tenemos suerte), entonces cuando estemos probando nuestro programa el compilador estará accesible en todo momento, además nuestro programa no terminará a menos que nosotros lo cerremos explícitamente (como todo los programas de Windows). Debemos tener cuidado de cerrar el programa en ejecución y no el compilador. Este es un error es muy común, ya que la ventana esquema del compilador es muy parecida a la aplicación en uso, de hecho, solo se diferencia por los puntos de la grilla. Si nosotros no cerramos la aplicación en prueba, podremos pasar el control al compilador y seguir programando tranquilamente, pero no nos permitirá recompilar el proyecto hasta que la aplicación no este cerrada. EDI – Laboratiorio Programación C++ Prof. Abdala Pablo
78
Ventana Principal, Botones, Cuadros de Edición.
Creación de un nuevo proyecto: Estamos en condiciones de crear nuestra primera aplicación, para ello, lo primero que haremos es guardar el proyecto como se explico en el capitulo 1. Si en estas condiciones, y sin tocar absolutamente compilamos (presionamos F9 o hacemos click en el botón de Play) el programa realizara las tareas necesarias y luego aparecerá nuestra aplicación, que no es otra cosa que una ventana completamente vacía. Sin embargo podremos ver que esta ventana es funcional, es decir, podemos moverla, maximizarla, minimizarla, y por supuesto cerrarla. Esto significa que los objetos que nosotros colocamos en la aplicación no necesitamos "programarlos" solo necesitamos indicar que deseamos hacer con ellos. Podemos ahora realizar nuestro primer programa, el clásico mensaje de bienvenida. Ejemplo: Realizaremos un programa que muestre una ventana con un botón, al presionar el botón aparecerá un mensaje que q ue diga: “Felicitaciones, ha realizado su primer programa”
Para ello colocaremos en la ventana esquema un botón, este objeto se encuentra en la barra de componentes en la pestaña Standar (El objeto con forma de botón que dice Ok). Ahora iremos al Inspector de Objetos, donde figuran las propiedades del botón que acabamos de crear. Atención, en el inspector de objetos figuran dos posibles objetos, o bjetos, la ventana (Form1) y el botón (Button1). No olvidemos, la ventana es también un objeto. Seleccionamos el objeto Button1 (nuestro botón) y hacemos click sobre la pestaña Events. Aquí vemos una lista l ista de los posibles eventos que puede considerar nuestro objeto, en nuestro caso, el evento que nos interesa es OnClick, así que hacemos doble click sobre este evento. Al hacer esto se abre una ventana con texto, el cursor esta ubicado entre dos llaves, que encierran la función (o cápsula) que debe ejecutarse cuando suceda el evento click del botón. Más arriba están las llamadas a librerías graficas y otras declaraciones que nosotros no debemos tocar. Esta ventana de texto es el código de nuestro programa, si pensabas que en la programación visual no había que escribir código te equivocabas. En esta ventana de texto escribiremos el código que se guardará como un archivo de texto *.CPP. Pero volvamos al cursor que está entre llaves. Dentro de estas llaves nosotros escribiremos el código que queramos, código que, en general ya conoces : (for, while, do-while, if, switch, etc.) con la salvedad de que algunas funciones que utilizábamos en la programación en modo texto ya no tienen sentido (como por ejemplo printf, scanf, gotoxy, etc.), debemos entonces aprender otras funciones pero esto no va presentarte mayores problemas. EDI – Laboratiorio Programación C++ Prof. Abdala Pablo
79
Nosotros queremos que al presionar el botón aparezca un mensaje. No necesitamos escribir algo como: if (boton = = pulsado) {......................} Esto no es necesario ya que lo que escribamos aquí se ejecutará cuando suceda el evento click del botón, así que solamente escribiremos lo que deseamos hacer cuando el botón se pulse. Deseamos que aparezca un mensaje, una función para mostrar mensajes es ShowMessage. Luego veremos que existen otras más complejas que nos brindan mayores opciones, ShowMessage es la más simple de todas, lo que hace es mostrar un mensaje en un cuadro de dialogo (sin ningún icono) y un botón de Ok que cierra el cuadro de dialogo. Escribiremos entonces entre las llaves: ShowMessage("Felicitaciones, ha realizado su primer programa"); De esta manera el programa completo debería haber quedado: //--------------------------------------------------------------------------#include #pragma hdrstop #include "Unit1.h" //--------------------------------------------------------------------------#pragma package(smart_init) #pragma resource "*.dfm" TForm1 *Form1; //--------------------------------------------------------------------------__fastcall TForm1::TForm1(TComponent* Owner) : TForm(Owner) { } //--------------------------------------------------------------------------void __fastcall TForm1::Button1Click(TObject *Sender) { ShowMessage("Felicitaciones, ha realizado su primer programa"); }
Recuerda, la primer parte del código, está generada por el compilador y no será necesario (ni recomendable) cambiarla. Solo nos interesa lo que ponemos dentro de las cápsulas de los eventos, en nuestro caso: ShowMessage("Felicitaciones, ha realizado su primer programa"); Compila el programa y pruébalo.
Analicemos ahora la primer función que has aprendido, la función ShowMessage, seguramente intuirás que esta función, pide como parámetro de entrada un string, y no devuelve nada. En realidad no pide un string, sino un nuevo tipo de dato llamado AnsiString, que por el momento te diré que es un string mejorado, luego aprenderemos más sobre los AnsiString. Lo que está escrito entre las llaves del evento OnClick es algo parecido a una funciones, es importante que sepas que las variables que declares en este punto se crearan al señalarse el evento y se destruyen al llegar a la llave de cierre. Son variables locales de esta cápsula. Con este programa sencillo podremos analizar algunas características de los objetos. EDI – Laboratiorio Programación C++ Prof. Abdala Pablo
80
Dentro de la programación orientada a objetos, uno de los conceptos principales es el concepto de herencia. Pongamos un ejemplo de la vida real. Supongamos que definimos el conjunto vehículos terrestres. Los vehículos tendrán la propiedad Cantidad de Ruedas, que será de tipo entero (2 ruedas, 4 ruedas, etc.) . Luego podríamos decir que dentro de los vehículos terrestres definimos el conjunto de motos y de automóviles. El conjunto de automóviles tendrá la propiedad Cantidad de Puertas (2, 4, 5). Es evidente que el conjunto de motos no tendrá esa propiedad, pero sin embargo, entre las motos y los autos habrá muchas propiedades en común (potencia del motor, velocidad máxima, etc.) En programación sucede lo mismo, lo cual significa que los objetos tendrán muchas propiedades en común (por suerte) con lo cual aprenderemos para que sirven algunas propiedades genéricas y luego veremos algunas propiedades pro piedades que son únicas de un determinado objeobj eto. Por ejemplo, todos los objetos visuales derivan de una clase genérica llamada Trect (Rectángulo). Esto significa que todos los objetos visuales tendrán la propiedad Top (distancia a la parte superior de la ventana medida en pixeles) Left (distancia a la parte izquierda de la ventana medida en pixeles) Height (altura del objeto medida en pixeles) y Width (ancho del objeto medido en pixeles). Veamos en el inspector de objetos algunas propiedades comunes a los objetos visuales, puedes analizar entonces tanto las de la ventana (Form1) como las del botón (Button1). Las propiedades aparecen en el inspector de objetos objetos en orden alfabético: alfabético: Propiedades de los objetos visuales: Caption: Es una propiedad de tipo AnsiString, es el título del objeto, en el caso de la ventana, el titulo que aparece arriba, en el caso del botón, el texto que aparece dentro del mismo. El Caption de un objeto es informativo para el usuario, debe ser claro, puede contener números, espacios, símbolos, etc. Caption inclusive podría estar vacía. Muchos objetos pueden tener el mismo Caption (en una aplicación hay muchos botones que dicen Aceptar). Si deseamos que un objeto (por ejemplo un botón) tenga una tecla rápida, antepondremos un & a esa letra, por ejemplo si tenemos un botón que dice Aceptar y otro que dice Archivo, y los Caption de ellos son respectivamente &Aceptar, y A&rchivo entonces veremos Subrayada la letra A en Aceptar y la letra r en Archivo. Pulsar la “A” equivaldrá a pulsar Aceptar, pulsar p ulsar la “r” equivaldrá a pulsar Archivo.
Cursor: Propiedad de tipo Enum Es la forma que toma el puntero del mouse al pasar por encima del objeto. Enabled: Propiedad de tipo booleana, puede valer true o false. Indica si el objeto esta habilitado o no. Un objeto deshabilitado aparece con su texto en relieve, y no es posible clickear sobre el. Font: Es una propiedad de tipo Tfont (una clase que guarda la fuente, el tamaño, y el estilo). Indica que fuente debe utilizarse en el Caption y en el texto (si el objeto lo tuviese). Height: Propiedad de tipo int. Altura del objeto medida en pixeles. EDI – Laboratiorio Programación C++ Prof. Abdala Pablo
81
Hint: Propiedad AnsiString, es el mensaje aclaratorio que deseamos que aparezca si el mouse se detiene sobre el objeto. Left: Propiedad de tipo int. Distancia del objeto medida en pixeles al borde izquierdo de la ventana madre. Name: Esta propiedad es muy importante, es el nombre interno del objeto, es decir, el nombre con el cual el programador (es decir nosotros) identificaremos al objeto, no tiene nada que ver con el Caption. Cuando programábamos en modo texto las variables tenían un nombre (A, B, C, CONT, PROM, etc.) sin embargo este nombre no tenía ninguna importancia para el usuario. Este es el mismo caso, Name es el nombre del objeto. Dos objetos no pueden tener el mismo nombre. Los nombres pueden contener, letras, números y el símbolo guión bajo “_”
pero el nombre de un un objeto no puede comenzar comenzar con un número, es decir, un objeto puede llamarse Boton1, pero no puede llamarse 1Boton. También sería un nombre válido Boton_Aceptar. Los nombres no pueden contener espacios en blanco u otros símbolos que no sean el guión bajo. Cuando el compilador crea un objeto, por defecto le pone el nombre del tipo de objeto seguido de un número, por ejemplo si creamos 3 botones se llamarán, Button1, Button2, Button3. Es muy recomendable ponerles un nombre mas específico que nos de una idea que hace el objeto. Sin embargo es importante que, si pensamos cambiarle el nombre al objeto lo hagamos ni bien lo creamos, ya que si lo cambiamos luego, el programa no actualizará las referencias. Por ejemplo, si nosotros en el programa del ejemplo cambiamos el nombre de Button1 por el de B_Aceptar, el compilador nos dará error, ya que el evento hace referencia a:
……..void __fastcall TForm1:: Button1Click(TObject *Sender)…….
Es decir que si cambiamos el nombre nomb re de un objeto, deberemos cambiar las referencias a eventos que tienen el nombre anterior. En resumen, cambiemos el nombre del objeto ni bien lo creamos o no lo cambiemos más. Top: Propiedad de tipo int. Distancia del objeto a la parte superior de a ventana madre. Visible: Propiedad de tipo booleana, puede valer true o false. Indica si el objeto es visible vis ible en pantalla o no. Width: Propiedad de tipo int. Ancho del objeto medido en pixeles.
Cuando analicemos nuevos objetos nos centraremos únicamente en las propiedades, métodos y eventos que revistan mayor interés en ellos. Pero en la mayoría de los casos tendrán propiepropi edades comunes a todos ellos (los citados arriba corresponden a los objetos visuales). Objeto Form Es una ventana, nuestro programa deberá tener al menos una ventana.
EDI – Laboratiorio Programación C++ Prof. Abdala Pablo
82
Propiedades: Por el momento las ventanas no tienen propiedades exclusivas que nos interesen (por ahora) Eventos: Podemos tratar diversos eventos en ella (OnClick por ejemplo) los nombres de sus eventos hacen evidentes su llamada. Métodos: Por el momento el único método que nos podría p odría interesar es el método CloCl ose() que obviamente cierra la ventana (si es la ventana principal cierra la aplicación)
Objeto Button: Es el botón simple de d e mandato: Sus propiedades y sus métodos no presentan mayor importancia, así como sus eventos (Salvo OnClick). Se usa generalmente para realizar una acción cuando sucede el evento OnClick.
¿Cómo accedemos a las propiedades de un objeto en tipo de ejecución?
Para acceder a la propiedad de un Objeto debemos primero hacer referencia al objeto (por su nombre) y luego la propiedad, separada del objeto por una flecha, que se escribe con un signo menos “- “ “ y un signo mayor “>”. “>”. Entonces, si quisiésemos cambiar el ancho ancho de Button1 para que valga 150, debemos escribir: Button->Width = 150; Si quisiésemos incrementar en 20 el alto de Form1 deberíamos escribir:
Form1->Height = Form1->Height + 20;
Atención con las mayúsculas y las minúsculas, minúsculas, recuerda que Builder es sensible a ellas.
EDI – Laboratiorio Programación C++ Prof. Abdala Pablo
83
Ya estas en condiciones de realizar algunos ejercicios sencillos.
Ejercicio 1: Realiza una aplicación con dos botones. Al pulsar el primero mostrar un mensaje de bienvenida. Al pulsar sobre el segundo, borra el título de la ventana.
Ejercicio 2: 2: Realiza una aplicación con cuatro botones, uno para aumentar el ancho de la ventana, otro para aumentar su altura, otro para disminuir su altura y el ultimo para disminuir su ancho.
Ejercicio 3: Realiza una aplicación con dos botones. Al pulsar sobre uno de ellos se habilita o deshabilita el otro botón, al pulsar sobre el otro, borrar los títulos de los dos botones.
Ejercicio 4: Realiza una aplicación con un botón. Al pulsarlo ocultarlo de la ventana (propiedad Visible) para volver a verlo pulsar sobre la ventana.
Objeto Edit: Veamos un nuevo objeto, el cuadro de edición simple. Es el objeto que se encuentra en la pestaña Standar y que tiene forma de cuadro cuadro con las letras ab dentro de el. Los cuadros cuadros de edición se utilizan generalmente para ingresar una línea de texto dentro de ellos (Ingresar nombres por ejemplo).
Propiedades: Las propiedades importantes del objeto Edit son: Text: Text: Propiedad de tipo AnsiString, es el contenido del cuadro de edición, aquí queda guardado el texto que figura en el. Por defecto esta propiedad contiene el nombre del cuadro de edición (si es el primero Edit1) E dit1) en general es preferible que esta propiedad arranque vacía. MaxLength: Propiedad de tipo entero. Indica la cantidad máxima de caracteres que se puede ingresar en el. PaswordChar: Propiedad de tipo char. Si deseamos que al ingresar un texto no se vea (por ejemplo para ingresar una clave) y que en su lugar aparezca un carácter determinado (por ejemplo *) colocamos en esta propiedad el carácter que deseamos. Por defecto esta propiedad contiene el carácter nulo, lo que significa que el texto se visualiza normalmente.
EDI – Laboratiorio Programación C++ Prof. Abdala Pablo
84
ReadOnly: Propiedad de tipo booleana, puede valer verdadero o falso. Indica si el cuadro es o no de solo lectura, en caso de serlo no se podrá modificar su texto desde la interfase visual.
Eventos: Uno de los eventos más utilizados en los Edit es el evento OnChange, que informa cuando el texto del Edit cambie (es decir, cuando se pulsa cualquier tecla que modifique su contenido), recuerda que el texto cambia en forma inmediata, es decir, no es necesario presionar ENTER para que el evento actúe.
Antes de realizar ejercicios utilizando cuadros edición deberemos aclarar que q ue son los famosos AnsiString. Los AnsiString son una clase, es decir, una estructura mejorada, mejorada porque tiene Métodos propios. Entre otras cosas, los AnsiString toman el tamaño necesario para contener el texto (son verdaderamente mas cómodos que los String) también tienen métodos para convertir los AnsiString en los tipos ya conocidos (int, float, string). Además de métodos para insertar, borrar, encontrar cadenas dentro de ellos etc. Nosotros veremos solo algunas cualidades de la clase AnsiString. Es importante que comprendas que AnsiString es una clase, esto es un tipo de dato. Nosotros podremos entonces declarar variables de este tipo, lo haremos de la forma habitual, primero el tipo y luego la o las variables. Ejemplo:
AnsiString cadena1, cadena2;
Declaramos dos variables de tipo AnsiString, cadena1 y cadena2. Bien, todo lo que a continuación veamos sobre AnsiString es aplicable a cualquier dato de este tipo. Por ejemplo la propiedad Caption es AnsiString, por lo que es factible aplicarle cualquier método que describamos a continuación.
Los AnsiString pueden ser igualados a cualquier tipo, la conversión es automática. Ejemplo:
AnsiString cadena; int j=10; char k[14]="hola que tal"; EDI – Laboratiorio Programación C++ Prof. Abdala Pablo
85
cadena=j; ShowMessage(cadena); cadena=k; ShowMessage(cadena);
Esto mostrará dos cuadros de dialogo sucesivos, el primero mostrará mostr ará un numero 10 (lo mostrará como texto, es decir, el carácter 1 y el carácter 0) luego, el segundo mostrará un mensaje que dirá “hola que tal”. Como verás, a una variable AnsiString se le puede asignar una
variable o una variable char sin problema, también se le pueden asignar variable float, pero en este caso habrá que especificar el formato (cantidad de cifras, separador s eparador decimal, etc.) Además se pueden efectuar operaciones de suma ente varios tipos, interpretándolo interpretándolo siempre como una concatenación de textos. Ejemplo:
AnsiString cadena; int j=15; char k[32]=" años de servicio en la empresa"; cadena="El señor Juan tiene"; cadena=cadena + j + k; ShowMessage(cadena);
Esto muestra un mensaje que dice “El señor Juan tiene 15 años de servicio en la empresa”
Como vemos a cadena se le asigna la suma de un AnsiString, un int y un string. La suma, fue interpretada como una concatenación de texto. Importante: Por el modo en que se realiza la conversación es imprescindible que el primer sumando sea de tipo AnsiString.
Los AnsiString pueden ser comparados unos con otros utilizándose en este caso el orden alfabético (como la hacíamos con los string de Pascal) Ahora veremos algunos métodos de la clase AnsiString. Los métodos se separan de la clase por un punto.
EDI – Laboratiorio Programación C++ Prof. Abdala Pablo
86
LowerCase: LowerCase: No tiene parámetros de entrada, devuelve un AnsiString, que es el AnsiSAnsi String propietario convertido a minúscula.
Ej: Form1->Caption= Form1->Caption.LowerCase();
UpperCase: UpperCase: Ídem LowerCase pero convierte a mayúscula.
Length: No tiene parámetros de entrada, devuelve un entero, el tamaño del AnsiString. Ej:
int j; j=Edit1->Text.Length();
j tomará el valor correspondiente a la cantidad de caracteres caracteres que tenga Edit1->Text. ToIntDef : Pide un entero, y devuelve un entero. ToIntDef convierte un AnsiString en entero, (es el valor que devuelve), en caso que el AnsiString no contenga un entero, entonces devuelve el valor pasado como parámetro.
Ej:
int j; j=Edit1->Text.ToIntDef(-3);
Si Edit1->Text no contiene un entero (es decir, cualquier cosa que no sea un número) entonces j tomará el valor –3.
Ejercicio 5: Realiza una aplicación con un botón y un Edit, al pulsar el botón, mostrar con ShowMessage el contenido del Edit.
Ejercicio 6: 6: Realiza una aplicación con un botón, al pulsarlo mostrar un mensaje que indique cuanta veces se pulsó el botón (necesitarás una variable global de tipo int).
Ejercicio 7: Realiza una aplicación con dos edit y un botón. Un Edit para ingresar nombre y el otro para ingresar edad. Al pulsar el botón, mostrar un mensaje que diga por ejemplo: “El señor Juan Perez es mayor de edad con 24 años”. O bien “Juan Perez P erez tiene 15 años y le faltan 3 para ser mayor de edad”. En caso de no haber ingresado un entero e ntero en la edad mostrar
un mensaje de error. EDI – Laboratiorio Programación C++ Prof. Abdala Pablo
87
Ejercicio 8: Idem al anterior pero si la edad no es un entero valido (positivo) y si el nombre esta vacío mantener el botón deshabilitado.
Ejercicio 9: Realiza una aplicación con un Edit y un botón, en el Edit se debe ingresar un número entre 1 y 10 (caso contrario el botón permanece deshabilitado). Al presionar el botón mostrar tantos mensajes como diga el Edit: “Mensaje Nº 1”, “Mensaje Nº 2”, etc.
Ejercicio 10: Realiza una aplicación con un Edit y un botón. Mientras el Edit este vacío, mantener el botón deshabilitado. Al pulsar puls ar el botón, cambiar el título de la ventana con el contenido del Edit.
Ejercicio 11: Utilizando un Edit y un botón, realiza el juego del ahorcado, (el algoritmo es casi el mismo que usaste en programación en modo texto con algunas modificaciones).
Es probable que para la realización de estos ejercicios te resulte conveniente utilizar el objeto Label.
Objeto Label: Label es simplemente una etiqueta, sus propiedades de interés son Caption (lo que dice la etiqueta) y Font (fuente de la etiqueta). Es factible tratar eventos sobre la etiqueta (OnClick por ejemplo) aunque en la práctica el objeto Label se usa como una etiqueta puramente aclaratoria.
EDI – Laboratiorio Programación C++ Prof. Abdala Pablo
88
Objeto Memo, archivos de texto
Veremos continuación el objeto Memo. Antes aclararemos algunos puntos. Los objetos son clases (que como lo habíamos mencionado, son estructuras mejoradas con métodos y eventos). Las propiedades de los objetos pueden ser clases que tienen métodos y eventos propios. Esto que en principio puede parecernos complicado, en realidad facilita muchísimo las cosas. Por ejemplo, muchos objetos visuales tienen la propiedad Color. La propiedad color es una clase (class) llamada TColor, la clase TColor tiene a su vez sus propiedades, pero lo importante, es que una vez que conocemos las propiedades de TColor, las podremos aplicar a cualquier objeto que tenga la propiedad Color, es mas, podremos hacer que un objeto tome el color de otro con una simple igualación, pues las propiedades Color de ambos objetos son del mismo tipo (TColor). Para facilitar las cosas los tipos de clase están dados anteponiendo una T mayúscula al nombre del objeto definido (los Button son de la clase TButton, los Form son de la clase TForm, etc.)
Objeto Memo: Es un objeto que tiene la capacidad de contener texto plano (esto es sin formato, es decir, sin cambios en la fuentes, colores etc. Contiene solamente texto, cambios de línea y tabulaciones. Para que te ubiques, el Bloc de Notas tiene un objeto Memo que ocupa toda la ventana principal. (No lo confundas con el WordPad, que puede contener distintos tipos de fuentes). El objeto Memo se encuentra en la pestaña Standar y tiene forma de hoja. Sus propiedades importantes son:
Color: de tipo TColor (común a muchos objetos visuales) hace referencia al color del fondo fondo memo. Font: Se refiere a la fuente con la cual visualizaremos el texto. Si bien el texto no contiene información acerca de fuentes y colores, todos los textos de Windows utilizan una determinada fuente. Esa fuente la especificamos con esta propiedad, pero el texto no guardara información sobre la fuente usada, se trata simplemente de la fuente con la cual deseamos ver el texto en este momento. ScrollBars: de tipo Enum, se refiere a si el objeto tendrá o no barras de desplazamiento en sus bordes, puede tomar los siguientes valores: ssNone : ninguna barra de desplazamiento ssHorizontal: barra de desplazamiento horizontal ssVertical: barra de desplazamiento vertical ssBoth: ambas barras de desplazamiento. Lines: de tipo TString, esto es una lista AnsiString. Es la propiedad que más nos interesa, ya que es la propiedad donde esta guardado el texto del Memo. TString es una clase, y es como se dijo una lista de AnsiString, a continuación te daré algunas propiedades y métodos de la clase TString, que será aplicable a todos los objetos de tipo TString (en particular a la propie
EDI – Laboratiorio Programación C++ Prof. Abdala Pablo
89
dad Lines de los Memos)
La Clase TString: Propiedades:
Count: Indica la cantidad de AnsiString que contiene el objeto TString Strings[int indice]: indice]: es una propiedad de tipo AnsiString, y se refiere al AnsiString que esta en la posición dada por el indice. Por ejemplo:
AnsiString aux; aux = Memo1->Lines->Strings[3];
este pequeño código copia el tercer renglón del memo en la variable aux (nota que el indice en este caso arranca de 1) Métodos:
Append (AnsiString S): Agrega el AnsiString S al final de la lista. Clear(): Borra toda la lista. Delete(int Delete(int indice): borra el renglón dado por índice (todo el texto se desplaza hacia arriba). Insert(int Insert(int indice, AnsiString S) Inserta el AnsiString AnsiString S en el renglón indice. Move(int Move(int actual, int nuevo): mueve el renglon actual al renglon nuevo. LoadFromFile(AnsiString LoadFromFile(AnsiString nombre_archivo): Carga un texto desde archivo dado por nombre_archivo SaveToFile(AnsiString SaveToFile(AnsiString nombre_archivo): Graba el el bre_archivo.
texto a un archivo dado por nom-
Con todo esto ya estamos en condiciones de realizar algunos programas: Ejercicios: Ejercicio 1: Realiza un programa que permita cargar y guardar un texto. Puedes especificar el EDI – Laboratiorio Programación C++ Prof. Abdala Pablo
90
nombre del archivo en un Edit.
Ejercicio 2: Idem anterior pero con botones para borrar el contenido del memo, y para informar mediante un mensaje la cantidad de renglones que contiene el Memo. Ejercicio 3: Idem anterior pero con un botón botó n que nos permita contar la cantidad de veces que aparece una letra determinada (ingresada en un cuadro de edición). Ejercicio 4: Modifica el programa del ahorcado para que en lugar de ingresar la palabra clave, la máquina la elija al azar desde un archivo de texto que contiene una lista de títulos de películas (el archivo te lo proporcionará el profesor). Puedes utilizar el objeto Shape (formas) mane jando su propiedad visible. Antes de terminar este capítulo veremos algunos componentes más cuya implementación es muy sencilla.
Objeto CheckBox: Es una casilla de verificación, que puede estar marcada o no. Se suele utilizar para manejar variables booleanas. Su propiedad más importante es la propiedad Checked, que puede tomar los valores true o false, de acuerdo a si la casilla esta marcada o no.
Objeto RadioGroup: Es un conjunto de casillas circulares. Se suele utilizar cuando debo elegir entre una de varias opciones, es decir, cuando cuando marco una casilla circular, circular, automáticamente se libera la casicasilla que estaba marcada anteriormente. Se puede configurar para que permita selección multiple, pero para este caso es preferible utilizar varias checkbox. Sus propiedades importantes son:
Items: Es una propiedad de tipo Tstrings, donde cada AnsiString representa una opción. Es factible entonces modificar los items en tiempo de ejecución. Esto en general no se utiliza, sino que se asignan las opciones en tiempo de d e edición. ItemIndex: Es una propiedad de tipo int, donde se representa el número de item que está seleccionado (0 es el primero) para que no haya haya ningún item selecionado, ItemIndex debe valer –1.
Ejercicio 6: Realiza un programa con un memo que me permita abrir y guardar texto. Debe haber una casilla de verificación,que verificación,que me permita poner visible o invisible el memo. memo. Ademas debe haber un radio group con las opcioes: EDI – Laboratiorio Programación C++ Prof. Abdala Pablo
91
Sin barra de deplazamiento. Solo barra horizontal Solo barra vertical Ambas barras Las cuales colocan las ScrollBar correspondientes en el memo.
Cuadros de dialogo dialogo y objetos no visuales
Como te habrás dado cuenta, ingresar los nombres de los archivos mediante cuadros cuadros de edición es muy incómodo y además da lugar a errores de tipéo. Para acceder a los archivos podemos utilizar una herramienta específica para tal fin.
Cuadros de Dialogo: Pensemos en cualquier programa de Windows que acceda a archivos (Word, Excel, Corel Draw, etc.) Al querer acceder a los lo s archivos se abre una ventana que nos permite movernos por el árbol de directorio, ver los archivos como lista, etc. Esta ventana es un cuadro de dialogo. Un cuadro de dialogo es una ventana que no puede ser redimencionada y puede ser tan compleja como queramos. De hecho, la ventana que aparece con ShowMessage es un cuadro de dialogo con un botón. Ahora bien, existen cuadros de dialogo predefinidos por Windows, por ejemplo, el cuadro de dialogo de “abrir archivos” es propio de Windows y es llamado por
los programas. Nosotros podremos entonces pedirle a Windows que muestre alguno de sus cuadros de Dialogo predefinidos. Para ellos deberemos colocar el objeto “cuadro de dialogo”
que necesitemos. Estos objetos están en la ventana Dialogs. Aquí se incorpora un concepto nuevo, los objetos no visuales. Es decir, cumplen una determinada función pero no son visibles para el usuario. Los cuadros de dialogo son objetos no visuales, al ponerlos en nuestro proyecto se verán como un icono, pero ese icono no aparecerá en nuestra aplicación, por lo tanto no es importante el lugar donde pongamos ese icono, es conveniente ponerlo en un lugar visible (para poder acceder a sus propiedades) pero que no nos moleste mientras estamos en editando.
Objeto OpenDialog: Dentro de la pestaña Dialogs, es el objeto que tiene forma de carpeta. Su función es obtener el nombre de un archivo. Como todos t odos los cuadros de dialogo es un objeto no visual. Métodos: Execute(): es un método que no pide ningún parámetro y devuelve un valor booleano. Es el método más importante del OpenDialog. Lo que hace hace es justamente mostrar (ejecutar) el cuadro de dialogo. Allí podremos entonces seleccionar cualquier archivo de nuestro sistema, nos permitirá cambiar de carpeta, de unidades, crear una nueva carpeta, etc. Hay dos maneras de cerrar el cuadro de dialogo, seleccionando un archivo, o cancelando la operación (ya sea con Cancelar o con el botón de cerrar la ventana). Si seleccionamos un archivo Execute() devolverá TRUE, si cancelamos la operación Execute devolverá FALSE. EDI – Laboratiorio Programación C++ Prof. Abdala Pablo
92
Es importante que tengas en claro que el objeto OpenDialog no abre un archivo, lo único que hace actualizar la propiedad FileName con el nombre del archivo seleccionado, o sea que la operación de apertura de archivo archivo corre cuenta del programador. Recuerda OpenDialog no abre un archivo, sino que nos permite seleccionar uno cómodamente. El método Execute() deberá ser llamado por el evento que corresponda cuando desees abrir un archivo (por ejemplo un botón de abrir).
Propiedades:
FileName: Propiedad de tipo AnsiString. Esta es la propiedad que contiene el nombre del archivo que se seleccionó con el método Execute(); y es la propiedad que nos interesará a la hora de abrir el archivo. Si el método Execute() se cancelo (devolvió FALSE) esta propiedad no varía, por lo tanto contendrá el mismo valor que antes. Debes tener cuidado de evaluar si el evento Execute() devuelve verdadero o falso para evitar errores. Ej:
void __fastcall TForm1::Button1Click(TObject *Sender) { if (OpenDialog1->Execute()) Memo1->Lines->LoadFromFile(OpenDialog1->FileName); } Este código carga el texto de un archivo en un Memo.
Title: Propiedad de tipo AnsiString. Aquí colocamos el título que deseamos que tenga el cuadro de edición. Si no se especifica nada, el título será s erá Abrir. Filter: La propiedad filter permite visualizar determinados tipos de archivos (solo *.txt, solo *.pas. etc.) Haciendo doble click sobre la propiedad filter (Inspector de Objetos) se abre el filter editor, que nos presenta una tabla con dos columnas. En la columna de la derecha colocamos el nombre del filtro (que deseamos que aparezca en el cuadro de dialogo), en la columna de la izquierda, el filtro en sí. Ej: Filter Name
Filter
Archivos de texto
*.txt
EDI – Laboratiorio Programación C++ Prof. Abdala Pablo
93
Archivos de C
*.cpp
Todos los archivos
*.*
DefaultExt: Propiedad de tipo AnsiString. Si lo deseamos podemos indicar una extensión que se agrega automáticamente si el usuario escribe el nombre de un archivo sin extensión. En general esta propiedad no se utiliza en el OpenDialog.
Objeto SaveDialog: Como se aclaró anteriormente, el objeto OpenDialog no abre un archivo sino que nos facilita la obtención de su nombre. Por lo tanto sería factible utilizar el OpenDialog para obtener el nombre de un archivo que deseamos guardar. Esto (insisto) es totalmente factible, sin embargo, las leyendas el cuadro de OpenDialog, hace referencia a apertura de archivo, con lo cual podría dar lugar a equivocaciones usar este cuadro para guardar. Para guardar entonces se utiliza el cuadro de dialogo SaveDialog, que tiene las mismas propiedades que el anterior, y el evento Execute() que se comporeta de la misma manera. La diferencia es puramente estética, ya que sus textos hacen referencia a Guardar en lugar de abrir. En este objeto cobra sentido la propiedad DefaultExt, ay que si el usuario no coloca ninguna extensión al archivo, SaveDialog colocará automáticamente la extensión que figura en dicha propiedad. Por ejemplo si se trata de un programa que trabaja sobre texto plano sería lógico que su DefaultExt fuese TXT.
Objeto ColorDialog: Al igual que los anteriores es un objeto no visual y se comporta de la misma forma. Se utiliza cada vez que deseamos seleccionar un color. El método Execute se comporta igual que en los cuadros anteriores. Propiedad Color: Es un propiedad de tipo TColor. La clase TColor guarda información acerca de un color específico, nos bastará con saber que podemos igualar dos variables (o propiedades) de tipo TColor. Recuerda que el objeto ColorDialog es un objeto no visual, por lo tanto su propiedad Color no hace referencia al color de dicho objeto, sino al color que fue seleccionado (en caso de que Execute devuelve TRUE)
EDI – Laboratiorio Programación C++ Prof. Abdala Pablo
94
Ej: void __fastcall TForm1::Button1Click(TObject *Sender) { if(ColorDialog1->Execute()) Form1->Color=ColorDialog1->Color; }
El pulsar un botón aparece un cuadro de selección de color, al elegir uno, la ventana toma dicho color.
Objeto FontDialog: Es prácticamente igual al objeto ColorDialog, solo que en este caso la propiedad que nos interesa es Font, y que al igual que antes, no es la fuente del objeto (pues no es visual) sino la fuente seleccionada.
Ejercicio 1: Modifica los ejercicios 3, 4, 5 y 6 del capítulo anterior, reemplazando los cuadros de edición por OpenDialog y SaveDialog respectivamente.
Ejercicio 2: Realiza un programa que nos permita cargar y guardar un Memo, borrar su contenido, cambiar su fuente y su color. Los cuadros de dialogo no son los únicos objetos no visuales. Por ahora veremos también los menús.
Objeto MainMenu: Como se indicó antes, es un objeto no visual. Lo encontramos en la pestaña Estándar. El MainMenu es el menú principal de la aplicación. Una ventana solo puede tener un MainMenú, sin embargo es factible (aunque improbable) que deseemos cambiar de menú en tiempo de ejecución. Por lo tanto es posible colocar mas de un MainMenu en una aplicación, aunque solo uno estará activado a la vez. El objeto Form tiene la propiedad Menu, en la cual se especifica cual de todos los MainMenu existentes es el que debe utilizarse. Cuando nosotros creamos el primer MainMenu (y en general el único) esta propiedad se actualiza automáticamente, así que en general no debes preocuparte por todo lo anterior. Para editar el MainMenu, debemos hacer doble click sobre dicho objeto, y veremos una tabla en la que colocaremos las diferentes opciones. EDI – Laboratiorio Programación C++ Prof. Abdala Pablo
95
En la primer fila se suelen poner los títulos (Archivo, Editar, Opciones, Etc.) y a apartir de la segunda fila se colocan los distintos items. Si queremos que aparezca una línea divisoria, debemos ingresar como Caption el carácter menos “- “. “. Si hacemos click derecho sobre un item, podemos (entre otras cosas) agregar en el un submenu, podemos además guardar y cargar una plantilla (template) con el esqueleto de menú que deseemos. Para acceder el evento que se llamará al clickear sobre el item del menu, lo único que debemos hacer es doble click sobre dicho item. Cada item tiene un nombre, de este modo podemos acceder a las propiedades de cada item (por ejemplo la propiedad Enabled de cada item) Colocar un menú a una aplicación es algo verdaderamente sencillo, irás aprendiendo más propiedadesy funciones con su uso.
Objeto PopUpMenu: El PopUpMenu es el menú emergente, esto es, el menú que aparece cuando hacemos click derecho sobre un determinado objeto. La manera de crear un PopUpMenu es idéntica a la del MainMenu. Podemos crear tantos PopUpMenu como deseemos, pero recuerda que estos están vinculados con objetos visuales. Todos los objetos visuales tienen la propiedad PopUpMenu, aquí especificamos que PopUpMenu deseamos relacionar con este objeto. Un mismo PopUpMenu puede estar vinculado a varios objetos visuales.
Ejercicio 3: Realiza un programa que nos permita cargar y guardar un Memo, Nuevo archivo, cambiar su fuente y su color. Todas estas funciones deben ser accesibles desde el menú principal, además las funciones de cambio de color y de fuente deberán ser accesibles desde un menú emergente relacionado al Memo.
Ejercicio 4: Idem al anterior, pero deben figurar las opciones Abrir, Guardar y Guardar Como. Al Abrir o Guardar Como debe copiarse el nombre del archivo en el título de la ventana. Si el archivo ya tiene un nombre válido entonces se habilitará la opción Guardar, la cual no deberá preguntar el nombre del archivo. Dicha opción debe deshabilitarse con Archivo Nuevo.
EDI – Laboratiorio Programación C++ Prof. Abdala Pablo
96
Cuadros de diálogo personalizados y ventanas múltiples.
Ventanas no modales: En este capítulo veremos como realizar nuestros propios cuadros de dialogo, los cuales podrán ser tan complejos como queramos, ya que de hecho, un cuadro de dialogo es una ventana que no puede ser redimensionada. El proceso es sencillo, pero requiere ser metódico a la hora de guardar nuestro proyecto.
Ejemplo 1: Para el ejemplo crearemos una ventana principal con un botón, luego, en el menú iremos a File->New Form. De esta manera se crea una segunda ventana llamada (en principio) Form2. Para no confundir esta ventana como la primera, le cambiaremos el color desde las propiedades (rojo por ejemplo) y le agregaremos dos botones, uno que diga Si, y otro que diga No. Es importante que grabes tu proyecto en una carpeta como se explicó en el capitulo 1. Si no cambias los nombres de los archivos (si los cambias no habrá ningún problema), la ventana 1 estara guardada como Unit1.cpp con su correspondiente Unit1.h, y la ventana2 estara guardada como Unit2.cpp con su correspondiente Unit2.h, El proyecto, al ser compilado arrancará con el Form1, el cual es independiente del Form2, y ademas desconoce la existencia de Form2, es por eso que necesitamos que Form1 se entere de que Form2 existe para llamarlo. Para eso iremos al código de Form1 (Unit1.cpp) y veremos que en el principio, dentro de los lo s include figura: #include "Unit1.h" Aquí se incluye la declaración de los objetos y los métodos del Form1. Lo que haremos será agregar a continuación la declaración de los objetos y los métodos del Form2, es decir agregaremos a continuacion: #include "Unit2.h" Con lo cual Form1 conocerá la existencia de Form2. Ahora haremos que al pulsar el botón del Form1 aparezcar el Form2. Para ello utilizaremos un método que posee el objeto Form, el método es Show() que como podrás darte cuenta lo que hace es justamente mostrar un Form. El código entonces quedará: EDI – Laboratiorio Programación C++ Prof. Abdala Pablo
97
//----------------------------------------- //--------------------- -----------------------------------------------------#include #pragma hdrstop #include "Unit1.h" #include "Unit2.h" //----------------------------------------- //--------------------- -----------------------------------------------------#pragma resource "*.dfm" TForm1 *Form1; //----------------------------------------- //----------------------- ------------------------------------------------- __fastcall TForm1::TForm1(TComponent* Owner) Owner) : TForm(Owner) { } //----------------------------------------- //--------------------- -----------------------------------------------------void __fastcall TForm1::Button1Click(TObject *Sender) { Form2->Show(); } Si pruebas de compilar esta aplicación verás que entonces ambas ventanas son accesibles, cada ventana tendrá entonces sus objetos y sus métodos en forma independiente. Este tipo de aplicación se llama aplicación SDI, donde puede haber varias ventanas, y donde podemos cambiar el foco de ventana en ventana en ventana. Mas allá de los nombres que tengan los objetos (si no has cambiado los nombres, cada ventana tendrá un botón llamado Button1), debes saber que cada objeto es propio de cada ventana.
Ejemplo 2: Para aclararlo, agrega un segundo botón en el Form1, y cuando lo pulse agrega el siguiente código:
Button2->Left++;
Lo que sucederá ahora es que cada vez que pulses este botón, el mismo se desplazará a la derecha. Pero si Form2 tiene un botón que también se llama Button2, ¿cómo sabe Builder a cual de los dos nos referimos? En realidad, el hecho de que los dos botones se llamen igual es pura coincidencia. Form1 no puede hacer referencia (en forma directa) a los objetos de Form2. Prueba de cambiar el nombre del Button2 del Form2, colócale como nombre nombre SegundoBoton. EDI – Laboratiorio Programación C++ Prof. Abdala Pablo
98
Y ahora modifica el evento del Button2 del Form1 de la siguiente siguiente manera:
SegundoBoton->Left++;
Supuestamente debería desplazarse el botón del Form2, sin embargo al compilar notamos que el compilador nos da un error
[C++ Error] Unit1.cpp(21): E2451 Undefined symbol 'SegundoBoton'
Lo que sucede es que Form1 no conoce a SegundoBoton, porque este objeto es propio de Form2, para ello deberemos modificar mo dificar nuestro código de la siguiente manera: Form2->SegundoBoton->Left++;
De esta manera podemos hacer accesibles los objetos de un Form desde otro Form. Cabe aclarar que por ahora los objetos de Form2 son accesibles desde Form1, pero no a la inversa, para lograr eso, en Unit2.cpp deberíamos incluir la declaración de los objetos de Form1, es decir:
#include "Unit1.h"
Cabe aclarar que los Objetos del Form2 son persistentes aunque la ventana se cierre, es decir, cerrar la ventana equivale a ocultarla, no a destruirla. Form1 sigue siendo la ventana principal de la aplicación, si se cierra la Form1, la aplicación se cierra, y Form2 se cierra con ella.
Ejercicio 1: Crea una aplicación aplicación con dos ventanas, una roja y la otra azul, ambas ventanas tienen un botón y un Edit. Al comenzar la aplicación deben estar ambas ventanas abiertas (utiliza el evento OnActivate de la ventana 1) . Al pulsar sobre el botón de la ventana 1, la ventana 2 tomará por título el contenido del Edit de la ventana 1 y viceversa .
Ejercicio 2: Modifica el ejercicio 4 del capítulo 4 de manera que el Memo este solo en una ventana diferente a la principal (para que el Memo ocupe toda la ventana dale a la propiedad Align el valor alClient). EDI – Laboratiorio Programación C++ Prof. Abdala Pablo
99
La diferencia entre ventanas y cuadros de dialogo es, como se ha dicho, que los cuadros de dialogo no pueden ser redimensionados, para ello lo único que hay que cambiar es la propiedad BorderStyle del Form2, y cambiarla de bsSizeable a bsDialog. De esta manera la ventana será un cuadro de dialogo. Prueba de modificar los ejercicios anteriores utilizando esta nueva propiedad.
Ventanas Modales: Hay una gran diferencia entre ventanas (o cuadros de dialogo) modales y no modales. La aplicación anterior tenía la particularidad de que nosotros podíamos cambiar el foco de una ventana a la otra, es decir, podíamos trabajar en una ventana o en otra indistintamente. Esto no siempre es así. Por ejemplo el cuadro de dialogo de ShowMessage toma el control de la aplicación, es decir, la aplicación pasa su atención a este cuadro de dialogo, el cual debemos cerrar para volver a tener el foco en la ventana principal (ya sea aceptando o cerrando el cuadro de dialogo). Lo mismo sucede con el cuadro de dialogo propio de OpenDialog. Este cuadro espera una respuesta (por selección o por Cancel) pero nosotros no podemos volver a nuestra aplicación sin dar una respuesta a este cuadro de dialogo. Este tipo de cuadros (o ventanas) son Modales. Un ejemplo de cuadro de dialogo no modal se el que aparece con la opción de hallar y reemplazar (en Word por ejemplo). En este caso si se nos permite cambiar el foco del Word al cuadro de dialogo sin necesidad de cerrarlo. Este es un ejemplo de cuadro de dialogo no modal. Las diferencias entre las ventanas o los cuadros de dialogo modales y no modales serían entonces:
No modales: Pueden coexistir con la ventana principal, y podemos cambiar de uno a otro cuando queramos. Modales: La aplicación pasa su atención a el, y no recupero el control de la aplicación hasta darle una respuesta (o cerrarlo).
Desde el punto de vista de la construcción de una ventana modal, los pasos a seguir son exactamente los mismos con una salvedad, en lugar de mostrarlos son el evento Show() los mostraremos con el evento ShowModal() que es propio del objeto Form.
Ejemplo 3: EDI – Laboratiorio Programación C++ Prof. Abdala Pablo
100
Sigue los pasos del ejemplo 1 pero en el evento del Button1 del Form1 en lugar de Form2->Show(); Coloca Form2->ShowModal();
Prueba la aplicación y verás que efectivamente no se puede pasar del Form2 al Form1 sin cerrar primero el Form2. Ahora bien, los botones que dicen Si y No son muy sugerentes, supuestamente deberían dar una respuesta. La idea de los cuadros de dialogo modales suelen ser dar un respuesta, recuerda los cuadros con los botones Si / No, Aceptar / Cancelar, Reintentar / Cancelar, etc. Aquí veremos una diferencia importante en los métodos Show() y ShowModal(). ShowModal(). Show es un método declarado como: void Show (void); Es decir, no pide ni devuelve nada, simplemente simpl emente muestra la ventana. ShowModal en cambio esta declarado como:
int ShowModal (void);
Es decir, ShowModal devuelve un valor entero, que es el que debemos evaluar para saber que botón a pulsado el usuario. ¿Cómo hacemos entonces para que la ventana modal devuelva un valor como respuesta? El proceso es muy sencillo. Los botones tienen (y siempre la tuvieron) una propiedad llamada ModalResult, que es una propiedad de tipo int. Al poner algo en esa propiedad, ese botón automáticamente se convierte en un botón que dará una respuesta a la ventana principal (devolviendo el valor especificado) y cerrará la ventana modal. Si observamos los valores que q ue puede tomar la propiedad ModalResult veremos que son:
MrOk MrCancel MrAbort MrRetry MrIgnore MrYes
EDI – Laboratiorio Programación C++ Prof. Abdala Pablo
101
MrNo MrAll MrNoToAll MrYesToAll Pero si ModalResult es una propiedad de tipo int, ¿cómo es posible que tome esos valores?. La respuesta es que todos los valores mencionados anteriormente son constantes que toman los valores del 1 al 10 respectivamente. Si quisiésemos dar otro valor entero diferente se puede asignar cualquier otro valor entero a ModalResult. Es nuestra responsabilidad evaluar el valor de ShowModal.
Ejemplo 4: Modifica el ejemplo anterior poniendo a los botones si y no del Form2 los ModalResul MrYes y MrNo respectivamente. En el evento OnCLick del Button1 del Form1 coloca lo siguiente: void __fastcall TForm1::Button1Click(TObject *Sender) { int r; r=Form2->ShowModal(); ShowMessage(r); } Prueba la aplicación y explica lo que sucede. Ejemplo 4: En el Form1 cambia el Caption del Button1 y coloca “Cerrar” En el Form2 agrega un Label con letras grandes que diga: “ ¿Seguro desea cerrar la aplic ación? ”
En el evento OnClick del Button1 B utton1 (cerrar) coloca: void __fastcall TForm1::Button1Click(TObject *Sender) { int r; r=Form2->ShowModal(); if(r==6) Form1->Close(); } EDI – Laboratiorio Programación C++ Prof. Abdala Pablo
102
Prueba la aplicación y explica que sucede.
Ejercicio 3: Modifica el ejercicio 2 de modo que al seleccionar Archivo Nuevo aparezca un cuadro de Dialogo Modal de confirmación (¿Esta seguro? Si o No). Ejercicio 4: Modifica el ejercicio anterior para que pida una confirmación cuando seleccionamos cerrar la ventana (consultar al profesor el evento OnCloseQuery).
Trabajos Prácticos Trabajo practico Estructuras de control Escribir los programas que se detallan a continuación. 1. La municipalidad debe liquidar impuestos atrasados de los últimos 10 años, los datos a ingresar son: Nro. De Contribuyente, Año, e Importe. Desarrollar un algoritmo que ingrese los datos e imprima la liquidación actualizando el importe de acuerdo al índice de la siguiente tabla. 1992-1994 1995 1996-1997 1998-2004 2005:2012
:3150 :3000 :2800 :2001 :2000
2.- Leer una temperatura e imprimir el deporte apropiado de acuerdo a la siguiente tabla: Temp > 28 28>-= Temp >= 20 20>-= Temp >= 15 15>-= Temp >= 5 -5 = Temp
:Waterpolo : Surf Surf :futbol :ajedrez :Snowboard
3.- Dados tres números hallar el mayor. 4.- Calcular la suma de los N primeros números naturales. 5.- Dado un número entero entero decir si: a) es par o impar; EDI – Laboratiorio Programación C++ Prof. Abdala Pablo
103
b) es mayor, menor o igual a cero. 6.- Dado un mes escribir la cantidad de días de dicho mes. 7.- Escribir las tablas de multiplicar del número 1 al número 9 de los primeros p rimeros 15 números. 8 Una empresa fabrica tapas tap as de material laminado en 3 formatos: redondo, cuadrado o rectangular. Cobra $18 el metro cuadrado y si la tapa es redonda, redonda, le suma $4 más al total. Se pide: a) Ingresar el código de forma: 1-redonda, 2- cuadrada, 3- rectangular b) Ingresar la longitud en metros: si es cuadrada, se ingresa un solo valor y si es redonda, corresponde al radio del círculo c) Informar el costo total de la tapa tapa 9.- Se ingresan pares de valores decimales y se debe informar el promedio de cada par. El ingreso de datos finaliza cuando el operador responde NO a la siguiente pregunta: “ Desea calcular el promedio? (SI / NO)?”
10.- En una empresa el sueldo se calcula adicionando al básico 50% del mismo, en caso en que la antigüedad sea superior a los 10 años. Diseñar un programa que lea el nombre del empleado, el sueldo básico y la antigüedad y escriba el nombre y el sueldo a cobrar. 12.- Ingresar 3 números, donde los dos primeros representan los extremos de un intervalo. Se solicita verificar si el valor pertenece o no al intervalo. 13.- Una empresa ha decidido dar a sus empleados una gratificación adicional adicional que depende de las horas extras y ausencias. Sea Horas= Horas Extras – (2/3)* ausencias. La gratificación G se calcula de la siguiente manera: Horas <= 10 G: 100 10< horas <=20 G: 200 20
104
17.- Diseñar un algoritmo que construya las facturas de electricidad correspondientes a un bimestre. Por cada usuario se lee nombre y domicilio y los estados del medidor anterior y actual (el fin de datos viene dado por un „fin‟). Las facturas deben contener la siguiente información con títulos aclaratorios: Nombre y domicilio: Estado del medidor actual: Estado del medidor anterior: Consumo del bimestre: Importe a Pagar: El importe se calcula en función del consumo de las siguiente s iguiente forma: Importe = 1.5 * consumo si consumo <= 100 kwh. Importe =2.0 * consumo si 100 kwh < consumo <= 200 kwh. Importe = 2.5 * consumo si consumo >200 kwh.
18.- El equipo de Hockey ha ha tenido una buena buena campaña y desea premiar a sus jugadores con un aumento de salario para la siguiente campaña. Los sueldos deben ajustarse de la siguiente forma: Sueldo Actual Aumento Hasta 4800
20%
4801- 6000
10%
6001 – 6600
5%
Mas de 6600
No hay
El equipo tiene un cuadro de 20 jugadores. Diseñe un algoritmo que lea el Nombre del Jugador y el salario Actual y que a continuación imprima el nombre, el sueldo actual y el monto aumentado. Al final de la lista debe proporcionar, también, el monto total de la nueva nómina que incluye los aumentos mencionados. 19.- Calcular la suma de los primeros 1000 múltiplos de 2. 20.- Que imprimen los siguientes códigos a)
b)
c)
d)
int x = 2, y = 6, z = 4;
int x = 2, y = 6, z = 4;
int x = 2, y = 6;
int i=4, x=5;
y = y+4*z;
if(x>y || x
if(x
for(i=0; i<10; i++)
y +=x;
cout<<"verdadero" ;
cout<<"verdadero";
{
cout<
else
else
if(i
cout<<"falso";
cout<<"falso";
else cout<
EDI – Laboratiorio Programación C++ Prof. Abdala Pablo
105
21.- Escribir un programa que acepte un año escrito en cifras arábigas y visualice el año escrito en números romanos, dentro del rango 1000 a 2100. Nota: recuerde que V =5, X=10, L=50, C=100, D=500, M= 1000. IV= 4, XL= 40, CM=900, MCM= 1900, MCML= 1950, MCMLXXXIX= 1989
Trabajo Practico Funciones 1. Escribir un programa que lea una cadena de caracteres y la visualice en un cuadro ********** *Algoritmos* ********** 2. Escribir un programa que lea una frase, sustituir todas las secuencias de dos o varios blancos por un solo blanco y visualizar la frase obtenida. 3. Escribir un programa que lea una frase y a continuación visualice cada palabra de la frase en una columna, seguido del número de letras que componen la frase. 4. Escribir un programa que posea una una función lógica llamada Vocal que determine si un carácter es una vocal. 5. Escribir un programa que permita al usuario elegir el cálculo del área de cualquiera de las figuras geométricas: círculo, cuadrado, rectángulo o triángulo mediante funciones. 7. Escribir un programa que posea una función que tenga un argumento de tipo entero y que devuelva la letra P si el número es positivo, po sitivo, y la letra N si es cero o negativo. 8. Escribir un programa que permita deducir si un número N es primo, apoyándose en una función llamada Primo. 9.- Escribir un programa que posea una función lógica de dos argumentos enteros, que devuelva true si uno es múltiplo del otro y false en caso contrario. 10.- Escribir una función inversa que recibe una cadena cad como parámetro y devuelve los caracteres de cad en orden inverso. Por ejemplo, si cad es cad es „Pablo?‟, la función devuelve „?o lbaP‟.
11. Que imprime el siguiente código a) int mi_funcion( ) { return 3+2; } int main( ){ cout<<”La function devuelde”<
106
b) int mi_funcion(int x) { return x*x; } int main( ){ cout<<”La function devuelde”<
Guía de trabajos Prácticos. Vectores y Matrices EDI – Laboratiorio Programación C++ Prof. Abdala Pablo
107
1) Leer un vector llamado Z de N elementos. A partir de su lectura calcular: a) Generar un vector llamado POSITIVO con la cantidad y el promedio de elementos positivos. b) Generar un vector llamado NEGATIVO con la cantidad y el promedio de elementos negativos. c) Generar un vector llamado ZERO con la cantidad y el promedio de elementos igual a 0. 2) Una compañía almacena la información relacionada a sus proveedores en los arreglos: - P(N) arreglo de proveedores, donde cada P(I) es el nombre del proveedor ordenado alfabéticamente - C(N) arreglo de ciudad, donde cada C(I) es el nombre de la ciudad en la que reside el proveedor P(I) - A(N) arreglo de artículos, donde cada A(I) es el número de artículos diferentes del proveedor P(I) Realice un programa en C++ que pueda llevar a cabo las siguientes transacciones: transacciones: Dado el nombre del proveedor, informar el nombre de la ciudad en la que reside y el número de artículos que provee. Actualizar el nombre de la ciudad, en caso de que un proveedor cambie de domicilio; los datos serán el nombre del proveedor y el nombre de la ciudad a la que se mudó. Actualizar el número de artículos de un proveedor en caso de que éste aumente o disminuya. La compañía da de baja a un proveedor: actualizar los arreglos. 3) Escribir un programa que lea un vector A de N elementos (N es un dato entero suministrado por el usuario). Una vez leído el vector, el programa debe permitir al usuario elegir a través de un menú la ejecución de las siguientes opciones: a) Volver a leer los datos d atos del vector b) Calcular el elemento mayor y menor del vector c) Calcular la suma de los elementos que componen el vector (ΣA[i]=A[1]+A[2]+…+A[N]) d) Calcular el promedio de los elementos que componen el vector (ΣA[i]/N)
e) Calcular el producto de los elementos que componen el vector f) Crear un nuevo vector que contenga los elementos del array transpuestos, es decir, B[1] contiene el elemento A[N], B[2] contiene el elemento A[N- 1], …, B[N] contiene el elemento A[1] g) Crear un nuevo vector que contenga los elementos del vector A pero con una posición corrida, es decir, B[1] contiene el elemento A[2], B[2] contiene el elemento A[3], …, B[N] contiene el elemento A[1] h) Crear un nuevo vector que contenga los elementos del vector A pero con M posiciones corridas (siendo M
i) Salir del programa 4) Se tiene un listado con los siguientes datos: EDI – Laboratiorio Programación C++ Prof. Abdala Pablo
108
número de alumno (1 a n ) (filas) número de materia (1 a m ) (columnas) nota (0 a 10). a) El mismo número de alumno y de materia puede aparecer más de una vez. b) El listado no está está ordenado, ni necesariamente completo. Esto último quiere decir que puede ser que un alumno no haya cursado una o más materias, y por po r lo tanto no existan los datos correspondientes en el listado. Se pide: (1) Crear una estructura bidimensional que almacene el promedio por materia de cada alumno e informarla asignándole en la impresión un guión al caso de falta de datos mencionado. (2) Informar el porcentaje de alumnos que cursó cada materia y el promedio general por materia considerando los alumnos que la cursaron. (3) Informar la cantidad de materias que cursó cada alumno y el promedio que obtuvo considerando las materias que cursó. 5) Realizar un programa que implemente el juego del BUSCAMINAS. BUSCAMINAS. Dicho juego consiste en lo siguiente: Existe una matriz bidimensional de NxM en la que se sitúan aleatóriamente K minas. Una vez distribuidas las minas en el tablero, el jugador especifica una casilla de la tabla, de manera que, si en dicha casilla existe una mina, el juego termina. Si en la casilla no existe mina, el programa debe devolver el número de minas que se encuentran en las casillas adyacentes a la casilla en cuestión, entendiendo por adyacentes todas aquellas casillas que se encuentren encima, debajo, a la izquierda, a la derecha, y en las cuatro esquinas. El juego se gana cuando el jugador es capaz de levantar todas las casillas libres del tablero sin haber “explotado” con ninguna mina. 6) Se posee una matriz de F filas y C columnas. a.- Asignarle valores a todos sus elementos elementos teniendo en cuenta cuenta que cada cada elemento a[i,j] está definido como si i*j es par el valor que se le asigna a la posición es i+j si i*j es impar el valor que se le asigna a la posición es i-j . Ejemplo: si f=2 y c=4 b.- Imprimir la matriz. 0 3 -2 5 3 4 5
6
7.- Leer una matriz de NxN elementos enteros llamada tierra. tierra. Generar un vector llamado agua que contenga los elementos de la matriz tierra remplazados en la siguiente función: X2*Pi+ 6X3. Calcular el promedio de los elementos que se encuentran en la diagonal superior y generar un vector llamado Aire tal que contenga los elementos de tierra multiplicados por aire. Y Generar un vector llamado l lamado Fuego de tipo char que contenga contenga los elementos elementos de la diagonal inferior convertidos en cadena de caracteres. Mostrar por pantalla Agua, fuego, tierra y aire. 8.- Ingresar una matriz A(10,8), calcular e informar la suma de sus s us elementos. EDI – Laboratiorio Programación C++ Prof. Abdala Pablo
109
9.- Leer una matriz de F filas y C columnas. (F =C) a.- Calcular el elemento Minimo de la l a matriz b.- Calcular el promedio de cada una de las filas. c.- Calcular el promedio de los elementos de la diagonal principal d.- Calcular el promedio de los elementos elementos de la diagonal secundaria. e.- A cada elemento par de la matriz guardarlo en un vector llamado Pares. 10. Entrada: Se tienen una matriz de N líneas, y M números para cada uno de estos i números, 0
Salida
1 1 1 1
1 3 5 2
2 1 1 3
2 1 3 1
3 5 5 3
3 3 3 5
8 7 3 2
11. Generar una matriz de NxN (N ingresada por el usuario < 100) con números aleatorios menores a 5000. Guardar en un Vector todos los números abundantes (función recursiva) que se encuentren en la matriz. Número abundante :
todo número natural que cumpla la condición que la suma de sus divisores propios sea mayor que el propio número. Por ejemplo, 12 es abundante ya que sus divisores son 1, 2, 3, 4 y 6 y se cumple que 1+2+3+4+6=16, que es mayor que el propio 12. Los primeros números abundantes son: 12, 18, 20, 24, 30, 36, 40, 42, 48, 54, 56, 60, 66, 70, 72, 78, 80, 84, 88, 90, 96, 100, 102, …
Trabajo Práctico Recursividad. 1.- Diseñar un programa que posea una función recursiva que permita multiplicar dos números enteros M y N. Acumulando su contenido hasta que uno de los lo s dos llegue a 0. 2.- Diseñar un programa que dado un numero decimal lo transforme a uno binario y a otro Hexadecimal. 3.- Escribir las funciones, una recursiva y otra no recursiva, tal que dado el entero positivo X devuelva true(verdadero) si y solo si X es potencia p otencia de 2. 4.- Escribir un programa que utilice una función recursiva EscribeBlancos(n) que imprima n caracteres blancos consecutivos.
EDI – Laboratiorio Programación C++ Prof. Abdala Pablo
110
5.- Diseñar un algoritmo recursivo que imprima los dígitos de un número decimal en orden inverso. 6.- Escribe una función recursiva que devuelva la suma de los valores almacenados en un array de enteros. 7.- Escribe un programa recursivo que calcule la suma de los primeros N números pares naturales. 8.- Implemente un programa que utilice una función recursiva que imprima por pantalla los valores desde 1 hasta el número introducido desde teclado por el usuario. 9.- Dado un vector de n números enteros, diseñar un algoritmo recursivo que dado el vector devuelva: · La posición del último número par que aparece en el vector · El valor 0 si ningún elemento del vector vector es par 10.- Diseñe una función recursiva que devuelva cierto si una palabra es palíndroma o falso en caso contrario. Decimos que una palabra es palíndroma si se lee igual de derecha a izquierda que de izquierda a derecha.
Trabajo Práctico archivos 1.- Crear un programa que vaya leyendo las frases que el usuario teclea y las guardar en un archivo de texto llamado “registroDeUsuario.out”. Terminará cuando la frase introducida sea "fin" (esa frase no deberá guardarse en el fichero). 2.-Hacer un programa que pida al usuario que teclee frases, y las almacene en el fichero “frases.txt”. Acabará cuando el usuario pulse -1. Después deberá mostrar el contenido del fichero por pantalla. 3.- Crear un programa que pida al usuario pares de números enteros y escriba su suma (con el formato "20 + 3 = 23") en pantalla y en un archivo llamado "sumas.txt", que se encontrará en un subdirectorio llamado "resultados". Cada vez que se ejecute el programa, deberá añadir los nuevos resultados a continuación de los resultados de las ejecuciones ej ecuciones anteriores.
Trabajo Práctico Punteros Ejercicio 1: Punteros ¿Qué imprime?. a) int *punt; int x=7; int y=5; punt=&x; *punt=4; Cout<
111
int y=5; punt=&x; x=4; Cout<<*punt<
e) int *punta, *puntb; int x=7; int y=5; punta=&x; *punta=3; puntb=&y; *puntb=x; x=9; Cout<<*puntb<
f) int *punta, *puntb; int x=7; int y=5; punta=&x; *punta=3; puntb=&y; *puntb=x; x=9; Cout<<*puntb<
112
int x=7; int y=5; punta=&x; *punta=3; puntb=&y; *puntb=x; x=9; puntb=punta; Cout<<*puntb<
113
*(punt)=7 ; punt[1]=11 ; for(i=0;i<5;i++) Cout<< *(punt+i) <<”/”<<; // ¿qué imprime este cout? ll) int *punt,i; int x[5]={1,2,3,4,5}; punt=&x[0]+3; *(punt-2)=9; punt--; *(punt)=7 ; punt[1]=11 ; punt=x; for(i=0;i<5;i++) Cout<< punt[i]<<”/”<<; // ¿qué imprime este cout? m) void suma_dos(int *x, int *y, int *z) { *x=*x+2; *y=*y+2; *z=*z+2; } int main(){ int x,y,z; x=3; y=10; z=15; suma_dos (&x, &y, &z); Cout<< x<<”/”<< y<<”/”<< z<<; // ¿qué imprime imprime este cout? } n) void datos(int *x, float *y, char *c) { *x=8; *y=4.2; *c=‟g‟; } int main(){ int x=9; float y=44.6; char c=‟a‟; datos (&x, &y, &c); Cout<< x<<”/”<< y<<”/”<< c<<; // ¿qué imprime este cout? } ñ) EDI – Laboratiorio Programación C++ Prof. Abdala Pablo
114
void datos(int *x, float *y, char *c) { Cout<< x<<”/”<< y<<”/”<
115
Cout<< x<<”/”<< y<<”/”<
r) #include void vector (float * vp[], float v[], int lon); void main(){ float v[5]={1,2,3,4,5}; float * vp[5]={NULL}; //vector de punteros a float for (int i=0; i<5; i++) { vp[i]=&v[i]; } vector(vp, v, 5); } void vector (float * vp[], float v[], int lon) { for (int i=0; i
s) #include #include void inverso_array (int v[], int inv[], int & lon) { for(int i=0; i
116
void main(){ int v[5]={2,3,4,5,6}; int inv[5]={0}; inverso_array(v, inv, 5); for (int i=0; i<5; i++) { cout << *(inv+i) << " "; } }
t) #include void vector (float * vp[], float v[], int lon); void main(){ float v[5]={1,2,3,4,5}; float * vp[5]={NULL}; //vector de punteros a float for (int i=0; i<5; i++) { vp[i]=&v[i]; } vector(vp, v, 5); } void vector (float * vp[], float v[], int lon) { for (int i=0; i
u) #include #pragma hdrstop #include #include #pragma argsused int I(char *s); int main() { char *s= "Pablo Abdala Achaval"; I(s); cout << s; EDI – Laboratiorio Programación C++ Prof. Abdala Pablo
117
system(“pause”); return 0; } //----int I(char *s) { char *t= s; while (*t) t++; t--; while(s < t) { char Temp= *s; *s++= *t; *t--= Temp; } }
Ejercicios Backtracking y Archivos 1.- Tom y Jerry Como es usual, el gato Tom persigue a Jerry por las habitaciones de una casa sin ningún éxito, pues el maldito roedor conoce todos los recovecos de la mansión. Harto de esta situación, Tom adquiere un PC con la intención de construir un programa capaz de encontrar el camino más corto hasta su ansiada presa sorteando todos los obstáculos (paredes, armarios, etc.) que pueda encontrarse. Además, para asegurar el éxito de la cacería, Tom desea atacar a Jerry cuando éste duerma. Representaremos la casa por una superficie rectangular de M filas y N columnas, dividida en casillas unitarias:
Tom tan solo puede moverse en horizontal o vertical dentro de la habitación, nunca en diagonal. Supondremos que tanto Tom como Jerry ocupan una casilla dentro de esta superficie. Los obstáculos de la habitación serán rectángulos de coordenadas válidas dentro de la superficie, y se representarán con su vértice superior izquierdo y el inferior derecho. Existe la posibilidad que el rectángulo sea en realidad una recta (horizontal o vertical; por ejemplo, las coordenadas (3, 4) y (3, 6) definen una recta horizontal) o incluso un punto (cuando las dos coordenadas son iguales).
EDI – Laboratiorio Programación C++ Prof. Abdala Pablo
118
Apartado 1 Construir un programa que procese un fichero de entrada, de nombre "TOM1.DAT", que contenga la configuración inicial de la casa, y que la dibuje si es correcta. Los casos de error que consideramos serán: (E0) M o N (o ambas) son igual a 0. (E1) Tom o Jerry no se encuentran en coordenadas válidas de la superficie de la casa. (E2) Tom y Jerry están en la misma casilla. (E3) Algún obstáculo no está situado en coordenadas válidas de la superficie. (E4) Los vértices que representan un obstáculo no cumplen una relación válida entre sí. (E5) Dos o más obstáculos se solapan. (E6) Tom o Jerry están en una casilla ocupada por un obstáculo. El formato del fichero "TOM1.DAT" es el siguiente: Línea 1: valores de M y de N, separados por un único espacio en blanco (tanto M como N se representan con un único dígito) Línea 2: cuatro valores naturales, separados por un único espacio en blanco, representados cada uno de ellos por un dígito. Los dos primeros valores representan la posición inicial de Tom (fila - columna) y los dos siguientes sig uientes la de Jerry. Líneas siguientes: cada línea se corresponde a un obstáculo y contiene cuatro valores naturales, separados por un único espacio en blanco, y representados cada uno de ellos por un dígito. Los dos primeros valores representan el vértice superior izquierdo del obstáculo (fila - columna) y los dos restantes el vértice inferior derecho. La salida debe almacenarse en el fichero "TOM1.RES". En caso de encontrarse algún error, el fichero contendrá una única línea con el mensaje "ERROR Ex", siendo la 'x' un número entre 0 y 6 que identifica el tipo del primer error que se encuentre en la entrada. Si hubiese más de un error a la vez, se devolverá aquel con código menor; por ejemplo, si el primer error lo provoca un obstáculo que se solapa con alguno de los anteriores y al mismo tiempo ocupa la casilla de Tom o de Jerry, el código a devolver sería E5. En caso de no encontrarse ningún error, el fichero contendrá M líneas de N caracteres cada una de ellas, siendo cada carácter el contenido de una casilla, que será 'o' para las casillas vacías, 'x' para las casillas que forman parte de un obstáculo, 'T' para la casilla ocupada por Tom y 'J' para la casilla ocupada por Jerry. A continuación, ofrecemos algunos ejemplos de entrada errónea y el mensaje que debe guardarse en el fichero de salida: TOM1.DAT
TOM1.DAT
TOM1.DAT
33 2233 1221
33 2232 1223
TOM1.RES
TOM1.RES
33 3223 1122 1223 TOM1.RES
ERROR E4
ERROR E6
ERROR E5
En cambio, las entradas siguientes generan resultados correctos: TOM1.DAT
TOM1.DAT
55
75
EDI – Laboratiorio Programación C++ Prof. Abdala Pablo
119
1153 2133 1444
1271 2122 2424 4245 6162 6464 TOM1.RES
TOM1.RES Tooxo xxxxo xxxxo oooxo ooJoo
oTooo xxoxo ooooo oxxxx ooooo xxoxo Joooo
Apartado 2 Construir un programa que, para una entrada que no contenga ningún error, devuelva un camino lo más corto posible que lleve de Tom a Jerry. El programa leerá la información del fichero "TOM2.DAT", con el mismo formato que el fichero "TOM1.DAT" del apartado anterior. La salida se almacenará en el fichero "TOM2.RES", que contendrá diferentes líneas con el resultado del algoritmo. En concreto, el fichero contendrá una línea para cada posición que forme parte del camino en el mismo orden en que aparecen en él, siendo pues la primera la posición de Tom y la última la posición de Jerry. Concretamente, cada línea contendrá un par de valores naturales, representados mediante un dígito y separados por un único espacio en blanco; el primer natural representa la fila y el segundo la columna. En caso de que no haya ninguna solución posible, el fichero "TOM2.RES" deberá contener una única línea con la palabra "INALCANZABLE". En caso de que haya más de un camino mínimo, podéis devolver cualquiera de ellos. Así para las entradas del último ejemplo del apartado anterior, para el primero de ellos el fichero "TOM2.RES" debería contener una única línea con la palabra "INALCANZABLE". En cambio, para el segundo, al existir un único camino mínimo, el resultado sería el fichero "TOM2.RES" siguiente: TOM2.RES 12 13 23 33 32 31 41 51 52 53 63 73 EDI – Laboratiorio Programación C++ Prof. Abdala Pablo
120
72 71
Apartado 3 Construir un programa que, para una entrada que no contenga ningún error, devuelva todos los caminos que lleven de Tom a Jerry, sean lo largos que sean, y tales que no se pasa más de una vez por una misma casilla. Para minimizar el tamaño de la salida, nos limitaremos a decir cuántos caminos hay de cada longitud. La longitud de un camino se define como el número de posiciones que lo componen menos uno; en el último ejemplo del apartado anterior, el camino que aparece tiene longitud 13. El programa leerá la información del fichero "TOM3.DAT", con el mismo formato que el fichero "TOM2.DAT" del apartado anterior. La salida se almacenará en el fichero "TOM3.RES", que contendrá diferentes líneas. Cada una de las líneas estará formada por dos valores naturales, representados mediante tantos dígitos como sea necesario y separados por un único espacio en blanco; el primer natural representa la longitud y el segundo el número de caminos existentes de esa longitud. No se debe incluir líneas para aquellas longitudes que no sean longitudes de algún camino de Tom a Jerry. Las líneas deben d eben estar ordenadas por la longitud. La figura siguiente muestra un ejemplo de entrada y el resultado esperado para ella: TOM3.DAT
TOM3.RES
44 1144 3233 2323
62 82
2.- Buque
La línea marítima "Titanic S.A.", cuya flota consta de un único buque, se ocupa del transporte de mercancías desde el puerto nuevo de Villabajo de Arriba allende los mares. Cada uno de los productos tiene un volumen (por motivos de conservación, las mercancías van siempre embaladas en cajas) y un precio de venta. Ante la convocatoria inminente de una huelga de estibadores, la línea decide efectuar un viaje extraordinario llenando el buque de manera que la mercancía que transporte sea lo más valiosa posible. Se pide construir un programa que, dada la información de las mercancías (volumen, precio y unidades disponibles), determine cuáles deben transportarse en el buque sin desbordar su capacidad (que será una información adicional sumistrada al programa). En caso que existan diversas combinaciones óptimas de mercancía con respecto al precio, se elegira aquélla que ocupe menos volumen; en este caso, sí puede suponerse que existe una única solución óptima al problema. Entrada Residente en el fichero de caracteres "BUQU.DAT": EDI – Laboratiorio Programación C++ Prof. Abdala Pablo
121
Línea 1: número N de tipos de mercancías, m ercancías, mediante uno o dos caracteres que representan un número entero entre 1 y 99. Línea 2: capacidad del buque, mediante uno, dos, tres o cuatro caracteres que representan un número entero entre 1 y 9999. Líneas de la 3 a la N+2: cada una de las líneas tiene el formato: mercancía volumen coste unidades donde mercancía es una palabra formada exclusivamente por letras minúsculas (y sin signos de puntuación, es decir, ni acentos ni similares) de a lo sumo 20 letras, y los otros o tros tres componentes son enteros representados mediante un número de dígitos que oscila o scila entre 1 y 5 (es decir, el entero más grande representable es el 99999). Los componentes de la línea están separados por un único carácter blanco, y no existen blancos ni otro o tro tipo de caracteres al principio o final de línea. Salida A guardar en el fichero de caracteres "BUQU.OUT": "BUQU.OUT": Línea 1: un par de enteros representados mediante dígitos de d e la manera habitual, que indican el coste total de la carga transportada y el volumen. Ambos valores están separados por un único carácter blanco, y no existen blancos ni otro tipo de caracteres al principio o final de línea. Líneas siguientes: cada una de ellas contiene un par: mercancía unidades que dice cuantas unidades de una mercancía dada aparecen en la carga transportada. La mercancía debe haber aparecido como tal en el fichero de entrada. Las unidades se representan mediante dígitos de la manera habitual. Ambos valores están es tán separados por un único carácter blanco, y no existen blancos ni otro tipo de caracteres al principio o final de línea. No deben aparecer mercancías con cero unidades transportadas. Las líneas deben estar ordenadas alfabéticamente según el nombre de la mercancía; no deben aparecer mercancías repetidas. Ejemplo de entrada 5 2000 patatas 350 2 7 judias 400 5 4 guisantes 1000 12 4 fresones 1100 17 3 arroz 600 8 1 Ejemplo de salida 27 1900 fresones 1 judias 2 3.- Estampillas
EDI – Laboratiorio Programación C++ Prof. Abdala Pablo
122
El servicio de correos quiere determinar la combinación óptima de valores de estampillas según ciertas características. Se sabe que la superficie estándar de las cartas permite que pueden pegarse hasta un máximo de h estampillas en el sobre. El servicio de correos está interesado en una emisión de estampillas de k valores diferentes a determinar, múltiplos de 1 $(no se admiten centavos). Dados h y k como se acaban de definir, se pide calcular el valor máximo n de pesos tal que se pueden generar todos los valores entre 1 y n pesos. Por ejemplo, si h = 3 (caben un máximo de 3 pesos en los sobres) y k = 2 (el servicio de correos contempla hasta 2 valores diferentes), el valor n es igual a 7, siendo los dos valores 1 y 3 pesos: 1 $ = 1 estampilla de 1 $ 2 $ = 2 estampillas de 1 $ 3 $ = 1 estampilla de 3 $ 4 $ = 1 estampilla de 3 $ + 1 estampilla de 1 $ 5 $ = 1 estampilla de 3 $ + 2 estampillas de 1 $ 6 $ = 2 estampillas de 3 $ 7 $ = 2 estampillas de 3 $ + 1 estampilla de 1 $ 8 $ = IMPOSIBLE Entrada Residente en el fichero de caracteres "ESTAM.DAT": varias líneas conteniendo cada una dos valores para h y k. Podéis suponer que los valores de h y k se pueden representar con una única cifra (carácter entre '1' y '9'). Las cifras se separan con un único blanco. No hay ningún otro tipo de caracteres ni al inicio ni al final de la línea. Salida A guardar en el fichero de caracteres "ESTAM.OUT": para cada línea de la entrada, una línea conteniendo: primero, el valor n, y segundo, los valores usados para generar este n, en orden creciente de valor. Estos datos se separan por un único blanco; no hay ningún otro tipo de caracteres ni al inicio ni al final de la línea. Ejemplo de entrada 32 21 Ejemplo de salida 713 21
4. El salto del caballo
Se dispone de una superficie rectangular "cuadriculada", es decir, formada por un entramado de posiciones o casillas (como ocurre, por ejemplo, con un tablero de ajedrez). Nos interesa buscar caminos dentro de esta superficie que cumplan ciertas condiciones, teniendo en cuenta que algunas de las casillas no pueden formar parte de dichos caminos (para entendernos, representan obstáculos en la superficie). EDI – Laboratiorio Programación C++ Prof. Abdala Pablo
123
Objetivo Dada una superficie rectangular de dimensión n x m, realizar un programa que la recorra partiendo de una casilla inicial y llegando a una casilla final según las reglas siguientes:
Todas las casillas deberán ser visitadas excepto las casillas marcadas como no visitables. No se podrá visitar más de una vez cada casilla. El salto de una casilla a otra deberá realizarse siguiendo las reglas de movimiento del caballo de ajedrez. Esto es, desde una casilla sólo se podrá avanzar a aquellas resultantes de avanzar una posición en línea recta y otra en diagonal en la misma dirección. El siguiente esquema muestra las casillas accesibles (con una cruz) desde la casilla A:
El programa deberá calcular la secuencia de casillas visitadas desde la inicial hasta la final. En caso que no exista ninguna solución, el programa debe detectar esta situación e informar. Entrada La entrada del programa consiste en una secuencia de líneas, que residen en un archivo de texto (ASCII) con nombre CAB.DAT, que tendrá el siguiente sig uiente formato:
La primera línea contiene la dimensión de la superficie, es decir, el número n de columnas y el número m de filas. La segunda línea contiene la posición inicial y la posición final, cada posición siendo dos enteros, la columna y la fila respectivamente. Podéis suponer que las dos posiciones están dentro de los límites de la superficie. A continuación, m líneas más. Cada línea representa una fila de la superficie, s uperficie, y contiene n caracteres, uno por columna. Cada uno de estos caracteres puede ser 'V' o 'N', dependiendo de si la posición correspondiente es visitable o no. Los caracteres aparecen separados por un único carácter "espacio".
A efectos de numeración, debe considerarse que las filas van de 1 a m y las columnas de 1 a n. Salida La salida del programa ha de grabarse en un archivo de texto (ASCII) con nombre CAB.RES, que contendrá una línea por cada posición que forme parte del camino encontrado como solución que cumpla las condiciones dadas en el enunciado. Cada posición es un par de enteros: la columna y la fila. La primera posición de la solución será la posición inicial, y la última la posición final. Si no hay ninguna solución, el fichero CAB.RES contendrá una única línea con el texto INSATISFACTIBLE. Si existe más de una solución, cualquiera de ellas se considera válida. Ejemplo de entrada EDI – Laboratiorio Programación C++ Prof. Abdala Pablo
124
54 11 14 V V V N N V N V V V V V V N V V V V V V Ejemplo de salida 11 23 44 52 31 12 24 32 53 34 13 21 42 54 33 14
5.- ANAGRAMAS Dado un conjunto de letras, escribe un programa que genere todas las palabras posibles. Ejemplo: De la palabra 'abc' el programa debe producir, explorando todas las posibles combinaciones de letras, las siguientes palabras: 'abc', 'acb', 'bac', 'bca', 'cab' y 'cba'. En la palabra obtenida del archivo de entrada, algunas letras pueden aparecer más de una vez. Para una palabra dada, el programa no debe producir la misma palabra más de una vez y las palabras deben aparecer ordenadas alfabéticamente. Los datos de entrada están en el archivo de texto ANAGRA.IN que está formado por diversas palabras. La primera línea contiene el número de palabras que siguen. Cada línea contiene una palabra. Una palabra está compuesta por letras de 'a' a 'z', del alfabeto sajón, mayúsculas y minúsculas. Las letras mayúsculas y minúsculas deben considerarse diferentes. estarán en el fichero de texto ANAGRA.OUT, para cada palabra del archivo de entrada, el archivo de salida debe contener todas las diferentes palabras que pueden ser generadas con las letras de la palabra. Las palabras generadas desde una palabra deben mostrarse en orden alfabético.
Los datos de salida
Ejemplo: EDI – Laboratiorio Programación C++ Prof. Abdala Pablo
125
ANAGRAMA.IN 2 Abc acba
ANAGRA.OUT ANAGRA.OUT
abc acb bac bca cab cba aabc aacb abac abca acab acba baac baca bcaa caab caba cbaa
6.-EL JUEGO DE LA VIDA Consideremos una población de K insectos en una tabla (M x N), de modo que en cada celda de la tabla hay, como máximo, un insecto. Por lo tanto, cada insecto tiene, como máximo, 8 vecinos. La población se está desarrollando continuamente debido a los nacimientos y defunciones que se producen. Las reglas de evolución que se observan son las siguientes: 1
Aquellos insectos que tienen 0, 1, 4, 5, 6, 7 u 8 vecinos mueren irremediablemente. 2 Los insectos que tienen 2 o 3 vecinos sobreviven. 3 En cada celda vacía en cuya vecindad hay exactamente tres insectos, nace un nuevo insecto. 4 Los insectos que nacen o mueren no afectan las reglas hasta que se ha completado un ciclo evolutivo, entendiendo por éste un ciclo en el que se ha decidido la supervivencia o muerte de los insectos (vivos al comenzar el ciclo) de acuerdo a las reglas mencionadas. Escribe un programa que simule la evolución de la población y que determine cómo estará la población después de L ciclos evolutivos. Los datos de entrada están en el archivo de texto VIDA.IN, en la primera línea hay 3 enteros separados por un espacio en blanco N, M y L que representan al número de filas, de columnas y de ciclos, respectivamente. Las siguientes N líneas contienen la ubicación de los insectos representados por un asterisco '*' y por un punto '.' las celdas vacías. Los datos de salida estarán en el fichero de texto VIDA.OUT, en el que habrá N líneas con la ubicación de los insectos tras L ciclos. En cada línea un '.' representa a una celda vacía y un '*' a un insecto. Ejemplo: VIDA.IN VIDA.IN VIDA.OUT VIDA.OUT
661 ...... ...**. ..**.. ...*.. ...... EDI – Laboratiorio Programación C++ Prof. Abdala Pablo
...... ..***. ..*... ..**.. ...... ...... 126
...... 7.- BIBLIOTECA INFANTIL
En una biblioteca infantil se ha diseñado un sistema para determinar cada día en qué orden elegirán los niños niños el libro que desean leer. Para ello los bibliotecarios han decidido seguir la siguiente estrategia: cada día se formará un círculo con todos los chicos. Uno de los chicos es elegido al azar como el número 1 y el resto son numerados en orden creciente hasta N siguiendo el sentido de las agujas del reloj. Comenzando desde 1 y moviéndose en sentido horario uno de los bibliotecarios cuenta k chicos mientras el otro bibliotecario comienza en N y se mueve en sentido antihorario contando m chicos. Los dos chicos en los que se paren los bibliotecarios son elegidos; si los dos bibliotecarios coinciden en el mismo niño, ese será el único elegido. Cada uno de los bibliotecarios comienza a contar de nuevo en el siguiente chicos que permanezca en el círculo y el proceso continúa hasta que no queda nadie. Tener en cuenta que los dos chicos abandonan simultáneamente el círculo, luego es posible que uno de los bibliotecarios cuente un chico ya seleccionado por el otro. Se pide la construcción de un programa que, dado el número N de chicos y los números k y m que utilizará cada bibliotecario en la selección, indique en qué orden irán siendo seleccionados los chicos. Formato de la entrada (residente en el fichero de caracteres "BIBLIO.IN"): En cada línea aparecerán tres números (N, k y m, los tres mayores que 0 y menores que 20) separados por un único espacio en blanco y sin blancos ni al comienzo ni al final de la línea. La última línea del fichero contendrá siempre tres ceros. Formato de la salida (a guardar en el fichero de caracteres " BIBLIO.OUT"): Para cada línea de datos del fichero de entrada (excepto, claro está, la última que contiene tres ceros), se generará una línea de números que especifique el orden en que serían seleccionados los chicos para esos valores de N, k y m. Cada número de la línea ocupará tres caracteres en el fichero (no llevarán ceros a la izquierda y serán completados con blancos por ese lado hasta alcanzar ese tamaño). Cuando dos niños son seleccionados simultáneamente, en el fichero aparecerá primero el elegido por el bibliotecario que cuenta en sentido horario. Los grupos de elegidos (de uno o dos niños cada uno) vendrán separados entre sí por comas (no debe ponerse una coma después del último grupo). La salida correspondiente a la entrada dada como ejemplo tendría que ser exactamente la siguiente; para ver con claridad cuántos blancos deben aparecer, representamos cada carácter blanco con un carácter '*' (en el fichero generado, pues, no aparecen asteriscos sino espacios en blanco). Ejemplo: BIBLIO.IN BIBLIO.OUT
10 4 3 528 13 2 2 000
**4**8,**9**5,**3**1,**2**6,*10,**7 **2**3,**5,**4**1 **2*12,**4*10,**6**8,**9**5,*13**1,**7,**3*11
8.- VA DE UNOS Dado cualquier número entero N del rango [1..10000] no divisible por 2 o 5, algún múltiplo de N es un número número que en notación decimal es una secuencia de unos. unos. Hay que encontrar el el número de dígitos que tiene el menor múltiplo de N que está formado solo sol o por unos. Por ejemplo, si N es 3, el menor múltiplo de 3 que está formado solo por unos es 111. Por lo tanto la solución sería 3. EDI – Laboratiorio Programación C++ Prof. Abdala Pablo
127
Los datos de entrada están en el fichero UNOS.IN, en el que hay una sola línea que contiene un número entero positivo del rango antes mencionado. Los datos de salida estarán en el fichero UNOS.OUT que contendrá una sola línea que contendrá el número de unos que tiene el menor múltiplo de N que está formado solo por unos. Ejemplo: UNOS.IN UNOS.OUT 7 6 9.- NOTACIÓN POLACA INVERSA Dada una expresión aritmética aritmética tal como la (a+b)*(c+d) (a+b)*(c+d) se puede representar con la notación polaca inversa de forma que cada operador va después de sus dos dos operandos de la forma: ab+cd+* Se trata de calcular una expresión dada en notación polaca inversa. Datos de entrada: El programa debe leer los datos de entrada del archivo NOPOIN.IN. En la primera línea figura la expresión a evaluar. Ésta está formada por enteros del rango [ – –32000..32000] y por los operadores +, -, * y /. Un espacio en blanco separa a cada operando y a cada operador. La expresión es sintácticamente si ntácticamente correcta. Datos de salida: El archivo de salida NOPOIN.OUT consistirá de una sola línea donde aparece el entero que es el valor de la expresión evaluada. EJEMPLO: NOPOIN.IN NOPOIN.OUT 10 8 + 4 2 - * 36 10.- Años Santos En el año 1179 el Papa Alejandro III, a través de la bula “Regis Aeterni”, confirma la
gracia del privilegio jubilar, concedida a Compostela por el Papa Calixto II a petición del arzobispo Diego Xelmírez, y establece oficialmente que serán años santos compostelanos todos aquellos en que la conmemoración del martirio de Santiago (el día 25 de julio) coincida en domingo. Desde entonces, ha habido además dos años santos extraordinarios: el 1885 (concedido por el papa León XIII mediante la bula “Deus Omnipotens”) para celebrar el redescubrimiento de los restos del apóstol, y el 1938 para que “pudieran asistir a ganar el jubileo los contendientes de la guerra civil que no hubiesen podido ir el año anterior”. Debe tenerse en cuenta que el actual calendario “gregoriano” fue establecido por el Papa Gregorio XIII en el año 1582 (mediante la bula “Inter gravissimus”) para mejorar la precisión del calendario “juliano” vigente en aquel momento. Para ello se corrigió la “regla de los bisiestos” (que indicaba que todos los años múltiplos de 4 tenían un día extra después del 28
de febrero), estableciendo que los múltiplos de 100 que no lo fuesen de 400 no serían, a partir de ese momento, bisiestos. Paracorregir la desviación que había provocado hasta entonces la imprecisión del calendario juliano, ese año se suprimieron los 10 días que van del 5 al 14 de octubre, de modo que al jueves 4 de octubre le siguió el viernes 15. Objetivo Se trata de calcular cuantos años santos hay entre dos años dados (ambos incluidos). Entrada En un archivo de texto con nombre "SANT.IN" se pasarán al programa una lista de pares de años (un par por línea), todos ellos posteriores al año 1179 y anteriores al año 100000. Los dos años de cada par aparecerán separados unicamente por espacios en blanco (uno o más). No habrá otros caracteres ni al principio ni al final de las líneas. Salida EDI – Laboratiorio Programación C++ Prof. Abdala Pablo
128
El programa deberá escribir, en un archivo de texto con nombre "SANT.OUT", una línea por cada línea del archivo de entrada, indicando para cada intervalo, el número de años santos s antos que le corresponde. Ejemplo
SANT.IN 1998 2000 1998 2004 1999 2005
SANT.OUT 1 2 2
11.- EL RECTÁNGULO DE KUBRIK El rectángulo de Kubrik es una adaptación a dos dimensiones del clásico cubo de Rubik. Se dispone de un rectángulo de 8 casillas, distribuidas en dos filas y cuatro columnas, coloreadas con 8 colores diferentes, que aquí representamos con un número del 1 al 8. Inicialmente, la disposición de los colores en las casillas es la siguiente: 1234 8765 Se pide, a partir de esta disposición inicial, alcanzar una disposición objetivo caracterizada por una configuración diferente del rectángulo. Un ejemplo podría ser la configuración final: 5183 4762 Para ello deberán aplicarse sobre la disposición inicial una serie de transformaciones hasta alcanzar la disposición objetivo. Existen tres tipos de transformaciones, identificadas por las letras A, B y C. A: intercambia la fila superior y la fila inferior. El efecto se visualiza en la figura siguiente: sig uiente: de la configuración de la izquierda se pasa a la de la derecha: 1234 8765 8765 1234 B: desplazamiento circular derecho del rectángulo: 1234 8765 4123 5876 C: rotación en sentido horario de los lo s cuatro cuadrados del centro: 1234 8765 1724 8635 Concretamente, el programa pide una secuencia mínima de transformaciones que lleve de la configuración inicial a la configuración destino. En caso de haber más de una secuencia mínima, se puede devolver cualquiera de ellas.
EDI – Laboratiorio Programación C++ Prof. Abdala Pablo
129
Formato de la entrada (residente en el fichero de caracteres "KUBRIK.IN"): una línea con ocho caracteres entre '1' y '8', sin repetición y separados exactamente por un blanco (no hay ningún otro tipo de caracteres ni al inicio ni al final de la línea), que representan la configuración final, numeradas a partir del vértice superior izquierdo en el sentido del movimiento de las agujas del reloj. Formato de la salida (a guardar en el fichero de caracteres "KUBRIK.OUT"): una línea inicial diciendo la longitud de la secuencia mínima y, a continuación, tantas líneas como transformaciones aplicadas, en el orden de aplicación. No debe aparecer ningún otro tipo de información en la línea. Podéis trabajar con la seguridad de que existe una secuencia de movimientos que llevan de la configuración inicial a la configuración final. Ejemplo:
KUBRIK.IN KUBRIK.OUT 51832674 4 C A B C
Laberinto ajedrecístico Descripción del problema Se tiene un laberinto muy particular que se desarrolla sobre un tablero cuadrado con lados de tamaño N. En cada avance se puede mover como caballo, alfil o torre, con la única restricción de no hacer más que dos movimientos de la misma especie en forma consecutiva. consecutiva. Así por ejemplo, sería ilícito mover como torre, caballo, caballo, caballo, caballo, alfil. Pero sí sería lícito: lícito: torre, caballo, caballo, alfil, caballo. El tablero tiene obstáculos, que limitan los movimientos máximos cuando se mueve como torre o alfil. Un movimiento como caballo sólo tiene la restricción de no posarse sobre un obstáculo. Como muchos obstáculos están contiguos y alineados con otros, ya sea horizontal, vertical o diagonalmente, se los ha descripto dando las ubicaciones de los extremos de tales alineaciones. Una secuencia “i, j, i, k” describe una alineación hor izontal; “j, i, k, i ” una vertical; y cuando el valor absoluto en la diferencia de las coordenadas extremas
es el mismo, la alineación es diagonal. Dos alineaciones de obstáculos pueden tener puntos en común. Se cuentan puntos en contra por cada jugada, uno por movimiento al modo de caballo, dos por movimiento al modo de alfil y tres por movimiento al modo de torre. El objetivo es partir del casillero [1, 1] y llegar al [N, N] acumulando la menor cantidad de puntos en en contra. Los casilleros de salida y llegada nunca tienen obstáculo y se sabe que el laberinto tiene solución. La figura siguiente ilustra dos posibles soluciones para el caso del ejemplo.
EDI – Laboratiorio Programación C++ Prof. Abdala Pablo
130
Se te pide que escribas un programa ajedrez.cpp, que informe la cantidad de jugadas de una solución óptima del laberinto, y los puntos en contra obtenidos. Datos de entrada Se recibe un archivo ajedrez.in del directorio actual, que contiene: • Primera línea: El tamaño tamaño N del tablero ( 3 ≤ N ≤ 1.000 ), ), la cantidad c de alineaciones alineaciones de obstáculos ( 1 ≤ c ≤ 2.000 ), separados por un espacio. • Un conjunto de c líneas, cada una conteniendo 4 números separados por blanco, correspo n-
dientes a una alineación de obstáculos. Datos de salida El programa debe generar el archivo ajedrez.out, en el directorio actual con: • Una línea, conteniendo la cantidad de jugadas de una solución del problema y la penaliz a-
ción obtenida, separadas por blanco.
EDI – Laboratiorio Programación C++ Prof. Abdala Pablo
131
Luchadores Japoneses
Descripción del problema Una academia dedicada a las difíciles artes de lucha oriental, está organizando un torneo de luchadores sumo. A los efectos de conformar la grilla de combates, es necesario armar armar parejas de luchadores que puedan ofrecer un espectáculo atractivo. Como es sabido que los cultores de esta disciplina son hombres de estructura física imponente, los organizadores habitualmente arman duplas de combatientes estableciendo comparaciones relativas a sus alturas y pesos. Por experiencia, los organizadores saben que un combatiente „domina‟ a otro si lo supera en
ambas medidas, o bien si lo iguala en peso y lo supera en altura, o viceversa. En cualquier otro caso, los luchadores no son comparables lo que hace el resultado imprevisible y por lo tanto más atractivo para el público. Sabiendo que no hay luchadores que coinciden en ambas medidas, los organizadores quieren quieren saber a cuantos posibles contrincantes domina cada uno uno de ellos, y por esta razón se te pide que escribas un programa sumo.cpp, que efectúe efectúe este recuento. Datos de entrada Se recibe un un archivo archivo sumo.in con el siguiente siguiente formato: formato: • Una línea que indica indica la cantidad L (1 ≤ L ≤ 100.000) 100.000) de luchadores. luchadores. • L líneas con 2 números números P y H (0 ≤ P,H ≤ 1.000.000) que indican indican el peso y la altura de cada cada
participante, separados por blancos. Datos de salida Se debe generar un archivo sumo.out conteniendo conteniendo • L líneas con la cantidad de luchadores luchadores a quienes domina cada cada participante, en el mismo or-
den en el que los participantes entraron.
EDI – Laboratiorio Programación C++ Prof. Abdala Pablo
132
Desinfectando los archivos Descripción del problema Los archivos de texto de una computadora han sido afectados por un nuevo virus. Este virus daña los archivos de texto de la siguiente manera: elige dos caracteres cualesquiera, luego recorre el texto y cada vez que encuentra dos ocurrencias consecutivas del primer carácter le intercala el segundo carácter. Por ejemplo si el texto original fuera fuera aadabeaa y el virus elige como como primer carácter a y como segundo carácter carácter b, el texto infectado infectado será abadabeaba. Si el archivo de texto original no contenía la secuencia aba entonces el archivo se puede desinfectar realizando el proceso inverso. A estos archivos los denominaremos desinfectables. Con el objeto de ayudar a desinfectar los archivos de texto de la computadora se te pide que escribas un programa antivirus.cpp, antivirus.cpp, que conociendo el texto infectado infectado y los dos caracteres elegidos por el virus, realice el proceso de desinfección del mismo. Tu programa sólo recibirá archivos que sean desinfectables. Datos de entrada Se recibe un un archivo antivirus.in con dos líneas:
EDI – Laboratiorio Programación C++ Prof. Abdala Pablo
133
• La primera línea contiene contiene la cantidad de caracteres del texto infectado, seguido de los dos
caracteres elegidos por el virus, separados por po r un espacio. • La segunda línea contiene contiene el texto infectado.
Datos de salida Se debe generar un archivo antivirus.out conteniendo dos líneas: • La primera línea contiene la cantidad de caracteres del texto desinfectado. • La segunda línea contiene contiene el texto desinfectado.
Restricciones •
Tanto los caracteres que elige el virus como los caracteres del texto pueden ser cualquier
letra del alfabeto, excepto la ñ, en minúsculas minúsculas y sin acentos. acentos. • La longitud máxima del texto a desinfectar es de 255 caracteres.
Hilera de ladrillos Descripción del problema Recientes hallazgos en ruinas del lejano oriente aportan datos sobre las formas que los esclavos tenían para distraerse durante las arduas jornadas en que se construyó la célebre muralla china. En un viejísimo manual de juego se detallan las siguientes reglas: se construye una hilera de ladrillos y se les asigna a cada uno un número. Pero esto siempre respetando la regla del juego, que indica que un ladrillo tiene que tener un número equivalente a la suma de sus dos ladrillos predecesores en la hilera, si los hubiera. Algunos ladrillos tienen número asignado y otros están vacíos. La idea es que se deben completar con números todos los ladrillos vacíos cumpliendo la regla anteriormente mencionada. Para completar las investigaciones, los excavadores nos han pedido escribir un programa ladrillos.cpp que encuentre a partir de una cantidad de ladrillos y algunos casilleros casilleros una forma de completar todos los números faltantes en los ladrillos de las hileras. Datos de entrada EDI – Laboratiorio Programación C++ Prof. Abdala Pablo
134
Los ladrillos vacíos los representaremos con un * (asterisco) y los números explícitos o desconocidos de cada ladrillo serán enteros de -1.000.000.000 a +1.000.000.000. Se recibe un archivo ladrillos.in del directorio actual, que contiene contiene la descripción de una hilera de ladrillos. La misma se describe por una primera línea que contiene un entero entero M, 1 ≤ M ≤
45, que representa la cantidad de ladrillos que componen la hilera. A continuación, en una segunda línea el archivo contiene M datos, separados por blancos, detallando en cada posición los números conocidos o espacios vacíos a completar mediante asteriscos. Datos de salida El programa debe generar el archivo ladrillos.out, en el directorio actual con la hilera de ladrillos completa (con todos sus casilleros definidos, con el formato de la entrada). Los casos con los que será probado el programa p rograma tienen solución con números enteros. Si hubiera más de una solución cualquiera vale.
A correr que es muy muy saludable … !!! !!!
Descripción del problema Una entidad deportiva ha organizado una carrera multitudinaria en el marco de los festejos por el Bicentenario. Los organizadores piensan clasificar clasificar a los competidores según su categoría categoría y sexo: Las categorías se asignan según rangos de edades y su cantidad puede variar de una competencia a otra, a criterio de quienes la organizan. Los sexos son naturalmente dos: masculino y femenino. Al inscribirse los corredores informan su edad y reciben un número que deben llevar abrochado en la remera. remera. Los números de corredor se asignan correlativamente. A medida que los competidores competidores van arribando uno detrás de otro a la meta, meta, los fiscales de la competencia registran el número del corredor. Se te pide que escribas un un programa carrera.cpp que determine los ganadores de oro, plata y bronce para cada categoría y sexo, para establecer los integrantes de los podios. Datos de entrada Se recibe un archivo carrera.in con el siguiente formato:
EDI – Laboratiorio Programación C++ Prof. Abdala Pablo
135
• Una línea que indica: la cantidad A ( 1 ≤ A ≤ 1.000.000 ) de competidores inscriptos, cf y cm ( 1 ≤ cf, cm ≤ 60 ) la cantidad de categorías por sexo respectivamente, y L ( 0 ≤ L ≤ to dos separados por blanco. Α ) la cantidad de corredores que arribaron a la meta, todos • cf líneas con los rangos Fn, Fx de edades de cada categoría femenina femenina (10 ≤ Fn ≤ Fx ≤ 80) y cm líneas con los rangos rangos de edades de cada categoría masculina Mn, Mx (10 ≤ Mn ≤ Mx ≤
80). Las categorías se dan en orden creciente; los rangos obviamente son disjuntos y sus cotas están separadas por blanco. • A líneas con con los pares E, X correspondientes a la edad ( 10 ≤ E ≤ 80 80 ) y sexo (M / F) de
los corredores inscriptos, en orden de inscripción (números correlativos, comenzando desde 1), separados por blanco. • L líneas con los números de los corredores corredores arribados a la meta, por orden de llegada.
Datos de salida Se debe generar un archivo carrera.out conteniendo • cf+cm líneas, conteniendo cada una el número de categoría y los 3 números de los corredo-
res que merecieron oro, plata y bronce, separados por blanco. blanco. Se deben listar ordenados por número de categoría, primero las cf correspondientes a las damas damas y luego las cm de los caballeros. Si en alguna categoría llegaron menos de 3 participantes, los premios correspondientes se declaran desiertos y se debe colocar un 0(cero) como número de corredor.
JUZGADOS Un funcionario de Juzgados tramita expedientes, de forma que siempre va tomando de la bandeja de expedientes a tramitar el que se encuentra más arriba. Cada expediente se puede caracterizar por un codigo_juzgado, id_receptoria, carátula, la fecha de inicio, la fecha último movimiento, tipo de causas: causas: en movimiento, paralizadas y archivadas. Un ordenanza es el encargado de traerle nuevos expedientes, que va dejando encima de los que ya había en la bandeja. EDI – Laboratiorio Programación C++ Prof. Abdala Pablo
136
a) Definir la estructura de datos más adecuada para representar la bandeja de expedientes y el empleado responsable del expediente. b) Realizar el código en c++ que permita representar el ingreso ingreso de expedientes nuevos que trae el ordenanza en la pila de datos, relacionados con el empleado del expediente. c)
Como con este sistema es fácil que algún expediente se retrase mucho, de vez en cuando un juez pide que se tramite un determinado expediente urgentemente. Para ello indica el código de dicho expediente. Definir la operación urgencia, urgencia, que permitirá buscar un determinado expediente en la bandeja y eliminarlo de la pila de expedientes expedientes del empleado.
MUDANZA (olimpiadas Mexico) Una familia va a cambiar de residencia y para ello va a utilizar dos camiones de mudanza. La empresa de mudanzas desea que todas las pertenencias de la familia se repartan equitativamente entre los dos camiones de manera que:
ambos transporten el mismo número de objetos, y el peso que carga un camión sea lo más parecido al peso que carga el otro, es decir, la diferencia entre los pesos debe ser la mínima.
Escribir un programa que dada la lista de los pesos de las pertenencias de la familia, determine el peso que cada camión deberá llevar para cumplir con los requerimientos de la empresa. Nota: No hay límite para el peso que pueden cargar los camiones. Entrada
La entrada deberá realizarse del archivo de texto mudanza.in. La primera línea contiene el número N de pertenencias de la familia (10 ≤ N ≤ 10000, con N par). Las siguientes N líneas contienen el peso de cada uno de los objetos representado como un entero positivo menor o igual a 60000.
Salida
En el archivo de texto mudanza.out deberá escribirse el peso que cargará cada uno de los camiones, en cualquier orden, separados por un espacio. Ambos pesos son números enteros.
Ejemplo: mudanza.in
mudanza.out
10
15871 17689
5320 21 34 17 100 286 9870 17605 12 295
EDI – Laboratiorio Programación C++ Prof. Abdala Pablo
137
Examen Diciembre 2012 – EDI 1.
Contenidos Asignatura EDI I: EJES EJES
PER PER ODO ODO CONTENIDOS (36 clases Aprox.)
Nº1
1
Introducción a la Programación
EDI – Laboratiorio Programación C++ Prof. Abdala Pablo
Conceptos iniciales Elementos técnicos básicos Lectura por teclado Tipos de soluciones. Abstracciones y modelos. Algoritmos. Lógica. Resolución algorítmica.
138
3
Nº2
Estructuras de Datos
Ciclos Repetitivos Nº3
5
Funciones y procedimientos
Nº4
5
Tratamiento de cadenas de caracteres
Nº5
7
Arreglos uni y bidimensionales
Nº6 Archivos
3
Nº6
10
Introducción a la recursión
Biblioteca iostream.h Estructura de un programa. Sintaxis y reglas sintácticas. Tipos de datos estándar en C++. Constantes y variables. Asignación y reglas de asignación. Operadores aritméticos y de relación. Entrada y salida de datos. (Cin y Cout) Condicionales simples y múltiples. (If) Control de ciclos condicionales. (while, do while) Control de ciclo exacto.(for) Declaración y utilización de funciones y procedimientos definidos por el usuario. Valores de retorno de una función. Argumentos de una función. Pasaje de parámetros por valor y por referencia Funciones que modifican parámetros. Copia, inserción y borrado dentro de cadenas de caracteres. Ubicación de caracteres dentro de una cadena. Tratamiento de cadenas como arreglos de caracteres individuales. Transformación de cadenas. Biblioteca stdlib.h Estructura de datos de arreglo. Manejo de dimensiones de un arreglo. Índices. Técnicas de ordenamiento (Selection Sort, BubbleSort, Inserción Directa, Método Shell, Heap Sort) Búsqueda secuencial y binaria. Arreglos paralelos. Matrices. Diagonal principal y secundaria Cálculo de matrices y vectores Archivos en C++. Abrir – cerrar – leer y guardar datos en archivos Recursión, concepto. La sucesión de Fibonacci, factorial de un número. Torres de Hanoi. Backtraking Introducción La vuelta del caballo El problema de las ocho reinas El problema de la mochila (selección óptima). Ejercicios Olimpiadas Informática Argentina – Resolución Nivel 2.
Nº7
2
OIA
Olimpiadas informatica Argentina EDI – Laboratiorio Programación C++ Prof. Abdala Pablo
139
Contenidos Asignatura EDI II: EJES
PERÍODO
CONTENIDOS
(36 clases Aprox.) IDE INTEGRATED DEVELOPMENT ENVIRONMENT
3
C++ Builder.. (Ventana principal. Diseñador de formularios. Editor de código)
http://elvex.ugr.es/decsai/builder/intro/2.html - PARTES_IDEBarra de Herramientas, Paleta de Compo-
nentes, Diseñador de Formularios, Inspector de Objetos, Editor de Código, Administrador de Proyectos Sistema de ayuda. Compilación, ejecución y depuración de programas. Almacenamiento de objetos.
MEMO MEMORI RIAA DIN DIN MICA - PUNTEROS
5
Introducción al uso de punteros Definición y declaración de punteros Generación y destrucción de variables dinámicas Operaciones básicas con datos apuntados Operaciones básicas con punteros El valor nil Aplicaciones no recursivas de los punteros Asignación de objetos no simples Funciones de resultado no simple
MECANISMOS DE ABSTRACCIÓN
5
Conceptos y clases Los miembros de la clase Control de acceso – constructores Miembros estáticos. Funciones miembro y funciones auxiliares
Sobrecarga de operadores
CLASES
SOBRECARGA DE OPERADORES
5
Uso de clases concretas Destructores constructores por defecto. New y delete. Notación Funciones del operador - operadores binarios y una-
rios significados predefinidos para operadores - significados definidos por el usuario para los operadores Operadores y espacios de nombres - un tipo complejo - miembros y no miembros operadores - de modo mixto aritméticas - inicialización - copia Conversiones - literales - Funciones auxiliares - Los operadores de conversión-ambigüedad resolución
EDI – Laboratiorio Programación C++ Prof. Abdala Pablo
140
friend - miembros y amigos - objetos de gran tamaño la asignación e inicialización - subíndices - llamada de función - desreferenciación - incremento y decremento - una clase de string. Conceptos y clases – Clases derivados - funciones miembro - Construcción y destrucción jerarquías de clase - Funciones virtuales Clases abstractas - jerarquías - jerarquías tradicionales de la clase clases abstractas como interfaces localización de la creación de objetos las clases abstractas y las jerarquías de clase templates - un String template instanciación - los parámetros de l template- Tipo de función comprobación de Template Deducción de parámetros del template la especificación de parámetros del template sobrecarga de función de template La política como argumentos del template los parametros por defecto del template. Especialización - derivación y plantillas - plantillas miembros - Las conversiones de código organización del código Creacion de proyecto final utilizando conocimientos adquiridos durante el año.
CLASES DERIVASAS
5
TEMPLATES
5
PROYECTO FINAL
5
Bibliografía
Stroustrup - Bjarne - The C++ Programming Language 3rd Edition- Addison Wesley Stroustrup - Programming - Principles and Practice Using C++ (Pearson, 2009). Herb Schildt C++ - The Complete Reference (3rd Ed). Grady Booch - Object-Oriented Analysis and Design With Applications, 2nd EDITION. Algorithms and Data Structures - Niklaus Wirth 2004. 2004. R. Sedgewick. Algorithms in C, C++(Fundamental Algorithms, Data Structures, Sorting, Searching). Addison-Wesley,1990. http://www.cplusplus.com http://es.scribd.com/doc/1739233/Ordenamiento-en-C http://www.algoritmia.net http://c.conclase.net http://www.cppreference.com http:/ /www.oia.org.ar /www.oia.org.ar (Olimpiadas Argentina de Informática) http://www.cartagena99.com/recursos/recursos_programacion.php http://www.nebrija.es/~abustind/Informatica/MetodologiaI/MetodologiaI.html http://www.nebrija.es/~abustind/Informatica/MetodologiaII/MetodologiaII.html http://elvex.ugr.es/decsai/builder/ http://courseware.ikor.org/internet
Programación Visual (Prof. Lazurri Guillermo – EEST Nº 5 – MdP)
EDI – Laboratiorio Programación C++ Prof. Abdala Pablo
141