domingo, 27 de mayo de 2018

Ejercicios Propuestos C++




Los ejercicios propuestos a continuación pretenden ofrecerte algunos problemas para que los hagas a medida que vas avanzando en el curso. Si tienes duda o inconveniente resolviendo algunos de ellos, comenta y te apoyaré al respecto. Si deseas ver cómo los he resuelto puedes verlos en mi cuenta de github.

Escribe un Programa en C++ que:
  1. Muestre en pantalla el mensaje "Bienvenido". 
  2. Muestre en pantalla el mensaje "C++ no tiene misterios". 
  3. Muestre en pantalla la suma de 100 y 120. 
  4. Muestre en pantalla el producto de 50 y 51.
  5. Guarde en la variable x (entera) el número 23, en la variable y (entera) el número 24, en la variable z (entera) el número 25 y muestre en pantalla la suma de los valores de las tres variables.  
  6. Guarde en la variable x (entera) el número 10, en la variable y (entera) el número 11, en la variable z (entera) el número 12, calcule su producto y lo guarde en una variable llamada producto, y finalmente muestre en pantalla el valor de la variable producto.  
  7. Pida al usuario dos números enteros (que se guardarán en las variables a y b) y muestre su suma en pantalla.  
  8. Pida al usuario dos números enteros (que se guardarán en las variables n1 y n2) y muestre su producto en pantalla.  
  9. Pida al usuario dos números reales (que se guardarán en las variables dato1 y dato2) y muestre en pantalla el resultado de dividir dato1 entre dato2.  
  10. Pida al usuario dos números reales (que se guardarán en las variables dato1 y dato2). Si dato2 es cero,deberá mostrar un mensaje de error, y en caso contrario mostrará en pantalla el resultado de dividir dato1 entre dato2.  
  11. Pida al usuario dos números enteros (que se guardarán en las variables num1 y num2). Si num2 es cero,deberá mostrar un mensaje de error, y en caso contrario mostrará en pantalla el resto de la división de num1 entre num2. 
  12. Pida al usuario un número entero, diga si es par o impar,y repita todo lo anterior hasta que el número que se introduzca sea 0. (Nota: para saber si un número es par, se mira si el resto de su división entre 2 es 0). 
  13. Pida al usuario veinte números enteros y muestre su suma. 
  14. Pida al usuario veinte números reales y muestre cual es el mayor de ellos y cual el menor.
  15. Pida al usuario un número indefinido de números (mientras se tecleen números que no sean negativos). Después de la introducción de cada número aparecerá en pantalla información sobre el número de datos introducidos y la suma hasta el momento, así­: "Has introducido 5 números y su suma es 38" Se termina al ingresar un número negativo. 
  16. Pida al usuario un número entero y muestre en pantalla sus divisores (exceptuando el 1 y el propio número). Por ejemplo, si se introduce 20, deberí­a aparecer:
    2 es divisor de 20
    4 es divisor de 20
    5 es divisor de 20
    10 es divisor de 20  
  17. Mejora el ejercicio anterior para que avise si el número no tiene divisores, en vez de que la pantalla quede en blanco. 
  18. Pida al usuario un número entero y una letra, y muestre en pantalla la letra repetida tantas veces como indique el número. Por ejemplo, si el número es 10 y la letra es a, aparecerí­a: aaaaaaaaaa 
  19. Pida al usuario un número entero y muestre su tabla de multiplicar. 
  20. Pida al usuario un número real y diga cuantas cifras enteras tiene (pista: habrá que dividir varias veces entre 10). 
  21. Pida al usuario que teclee 20 números, que se almacenarán en una tabla. Después se debe mostrar en pantalla los 10 primeros, en la misma línea de pantalla, separados por espacios en blanco. 
  22. Pida al usuario que introduzca una palabra y la muestre al revés (por ejemplo: hola -> aloh). 
  23. Pida al usuario que teclee 10 números, que se almacenarán en una tabla. Después se debe mostrar en pantalla la suma de todos ellos, la media, el valor mínimo y el valor máximo. 
  24. Pida al usuario que introduzca una frase y cambie los espacios por guiones (por ejemplo: "como estas, juan" -> "como-estas,-juan"). 
  25. Pida al usuario que teclee 10 números, que serán almacenados en una tabla. Después se deberán mostrar todos en la misma línea, separados por punto y coma, y decir si alguno está repetido. 
  26. Pida al usuario que introduzca un número y diga si es capicúa (si se lee igual de derecha a izquierda que de izquierda a derecha, como el 12321). (pista: será interesante leer el número como cadena de texto). 
  27. Pida al usuario que introduzca una contraseña. Si en un máximo de 3 intentos no escribe la contraseña correcta (por ejemplo, "hola"), recibirá como respuesta "Acceso denegado". Si acierta la contraseña en tres intentos o menos, se le dirá "Acceso permitido". 
  28. Pida al usuario que introduzca su nombre (que se guardará en una cadena de texto) y después su apellido (que se guardará en otra). El programa deberá crear una nueva cadena de texto formada por el el apellido y el nombre, separados por una coma, y luego mostrarla en pantalla, así:
    Hola Sr. Gates, Bill 
  29. Calcule el factorial del número que introduzca el usuario. El factorial de un número se calcula multiplicando ese número por todos los menores que él, hasta llegar al uno: 3! = 3 * 2 * 1
    6! = 6 * 5 * 4 * 3 * 2 * 1  
  30. Pida al usuario que teclee los datos de 10 personas (sólo el nombre y el número de teléfono). Después se deberá mostrar en pantalla los nombres y los teléfonos de aquellas personas cuyo número de teléfono comience por 6.  
  31. Sea capaz de almacenar datos de hasta 100 personas (sólo el nombre y el número de teléfono). Aparecerá un menú en pantalla que permita añadir una nueva persona, ver los nombres de todas las personas almacenadas, ver el teléfono de una cierta persona (a partir de sus nombre), corregir los datos de una persona o salir. 
  32. Crea una función llamada "suma", que reciba como parámetros dos números enteros y devuelva como resultado otro número entero que sea la suma de ambos. 
  33. Crea una función llamada "división", que reciba como parámetros dos números enteros y devuelva como resultado un número real, que sea el resultado de dividir el primer número entre el segundo (con decimales). 
  34. Crea una función llamada "letraRepetida", que reciba como parámetros una letra y un número, y escriba en pantalla esa letra repetida en pantalla varias veces (tantas como indique el número), sin devolver ningún valor. 
  35. Crea dos funciones "solucionRC1" y "solucionRC2" que devuelvan las dos raíces de una ecuación de segundo grado. Además, deberás crear una función "existeSolucionRC", que devuelva un 1 si la raíz cuadrada tiene solución, o un 0, si no la tiene.
  36. Crea una función "raizCubica", que calcule la raíz cúbica de un "float", y devuelva otro "float" (pista: puedes elevar a 1/3 para hallarla).

sábado, 26 de mayo de 2018

Estructuras en C++


La Estructuras es uno de los pocos temas tratados en la Programación Básica debido que al introducirse la posibilidad de la Programación Orientada a Objetos (POO), éstas quedan sustituidas por la aplicación de Clases. Sin embargo, por ser parte de una introducción a la Programación hemos decidido dedicar un capítulo a las Structs en C++.

Las Structs ofrece la posibilidad de reunir variados tipos de datos dentro de una sola estructura. Pensemos en lo siguiente, si queremos definir las características de una persona tendríamos que declarar variables tales como: nombre, apellido, edad, Nro. de Identificación, entre otros; lo que se volvería engorroso si deseamos almacenar esta información para 100 personas más, ya que debiera crearse 100 variables similares.

Para el caso anterior podría pensarse en crear un arreglo para cada variable y que cada índice este asociado a una persona en particular. Quizás sería una solución, sin embargo hay que procurar programar muy cuidadosamente la asignación de información para no mezclar los datos. 

De por sí, como programador, mi opción para estos casos es crear una estructura llamada persona que contenga esas variables y crear tantos objetos de la estructura persona como sean necesarias. Veamos de qué hablo.

Antes de iniciar con un ejemplo estudiemos su sintaxis:

struct nombre_de_estructura {
   int variable_1;
   float variable_2;
   char variable_3;
   ....
};

Fíjense que se inicia con la palabra struct para indicar que la definición corresponde a una estructura. Posteriormente se indica el nombre de la estructura y dentro de ella (entre llaves { }) se colocan las variables y/o arreglos relacionadas con la estructura definida. Por último se cierra la estructura con punto y coma (;). Tomando como ejemplo la estructura persona que describimos anteriormente, ésta quedaría así:
#include <stdlib.h>
#include <iostream>
using namespace std;

int main () {
   struct persona {
        char nombre[20];
        char apellido[20];
        int edad;
        int nro_iden;
    };
}

Notemos que se ha definido una estructura de nombre persona y sus atributos son dos arreglos tipo char con 20 espacios para almacenar el nombre y apellido de la persona, y dos variables tipo int para la edad y el número de identificación. Bien, qué te parece si continuamos con ese ejemplo y manipulamos sus atributos para conocer cómo se utilizan las estructuras.

#include <stdlib.h>
#include <iostream>
using namespace std;

int main () {
   struct persona {
        char nombre[20];
        char apellido[20];
        int edad;
        int nro_iden;
    };

    persona individuo1;
    persona individuo2;

    cout << "Introduzca los datos de la primera persona" << endl;
    cout << "Nombre: ";
    cin >> individuo1.nombre;
    cout << "Apellido: ";
    cin >> individuo1.apellido;
    cout << "Edad: ";
    cin >> individuo1.edad;
    cout << "Nro. de Identificación: ";
    cin >> individuo1.nro_iden;

    cout << "Introduzca los datos de la segunda persona" << endl;
    cout << "Nombre: ";
    cin >> individuo2.nombre;
    cout << "Apellido: ";
    cin >> individuo2.apellido;
    cout << "Edad: ";
    cin >> individuo2.edad;
    cout << "Nro. de Identificación: ";
    cin >> individuo2.nro_iden;

    cout << "La primera persona es " << individuo1.nombre << " " << individuo1.apellido << endl;
    cout << "Tiene " << individuo1.edad << "años y su identificación es " << individuo1.nro_iden;
    cout << endl;
    cout << "La segunda persona es " << individuo2.nombre << " " << individuo2.apellido << endl;
    cout << "Tiene " << individuo2.edad << "años y su identificación es " << individuo2.nro_iden;

    return 0;
}

Ahora, estudiemos este ejercicio:
  • Se ha creado una estructura cuyo nombre es persona con atributos nombre (arreglo tipo char), apellido (arreglo tipo char), edad (variable tipo int), nro_iden (variable tipo int).
  • Posteriormente se crean dos objeto que tendrán la estructura persona definida, estas son individuo1 e individuo2. Noten que se antecede el nombre persona en la declaración de los objetos, tal como si fuese un tipo de datos. Esto crea los objetos en la cual cada uno de ellos tendrán asociados los atributos definidos en la estructura.
  • La forma de acceder a los atributos es colocar el nombre del objeto, luego un punto y después el nombre del atributo. Por ejemplo: individuo1.edad me permite acceder el atributo edad del objeto individuo1. Esto es aplicable tanto para asignar un valor a ella (tal es el caso de cin) como para extraerla (con el cout se muestra la información en pantalla).
  • En el ejercicio se crean dos objetos con el propósito de mostrarles que se pueden crear tantos objetos con la estructura definida como se desee. Si requiriéramos 10 individuos con la información básica, sólo creamos 10 objetos con la estructura definida y manipulamos sus atributos para cada objeto.
Las estructuras es una herramienta muy poderosas a la hora de reunir una cantidad de variables y arreglos que mantienen relación para definir un objeto. Lo mismo se podría aplicar para cualquier cosa, por ejemplo una estructura triángulo con atributos: base, altura, tipo. Entre otros.

Espero hayan disfrutado de este capítulo. Para mí resulta interesante, ya que sirve como inicio para comprender la definición de Clases en POO. Nos vemos pronto, en el siguiente capítulo.

jueves, 24 de mayo de 2018

Punteros I: Introducción

Saludos estimados lectores. En el capítulo anterior se presentó el tema de las funciones en C++. Notemos que con ellas se cambia un poco el paradigma heredado de los lenguajes de bajo nivel adoptando, ahora, un enfoque más funcional, permitiendo segmentar ciertos bloques de código que cumplen un objetivo concreto y poderlo reutilizar en otros sectores del código principal sin la necesidad de copiar nuevamente el bloque de instrucciones. Esto inclusive fortalece el paradigma modular en la programación. Ahora nos enfrentamos a un nuevo tema: Punteros. Pero antes de comenzar con esto preguntémonos ¿Qué son los punteros? 

Un puntero es un objeto que apunta a la dirección de memoria de una variable, arreglo, función o estructuras más complejas, lo cual se constituye como una herramienta indispensable para los sistemas embebidos, además de proporcionarnos soporte para asignación de memoria dinámica, agregando, a su vez, otra dimensión al control de flujo en un programa.

Los usos más comunes para los punteros son:
  • Crean código eficiente y rápido.
  • Proporcionan asignación de memoria dinámica.
  • Hacen expresiones compactas y concisas.
  • Protegen datos pasados como parámetros a una función.
  • Proporcionan la capacidad de pasar estructuras de datos mediante un puntero sin ocasionar un exceso de código conocido como “overhead”.
En este curso, por ser básico, su interés es que sepas manejarlos con facilidad, dejando su aplicación de optimización para otros temas más avanzados. Por lo que se enfocará en cómo estos son utilizados para apuntar a una dirección de memoria de una variable. Lo que nos lleva a iniciar explicando el direccionamiento en las memorias.

Direcciones de Memorias

Al hablar de memoria nos referimos a la RAM, a la volátil, aquella que almacena información de manera momentánea para que nuestras aplicaciones puedan ejecutarse con rapidez y facilidad. Ahora para los efectos de programación veamos a la memoria como un gran bloque cuya capacidad esta definida por la unidad de medida el byte (8 bits). Ese bloque de memoria se encuentra segmentada en varios bloques más pequeños con capacidad de almacenamiento de un byte, en la cual cada una de esa división tiene asignada una dirección para ubicar mejor el dato almacenado.

Normalmente me gusta hacer comparación de esta visión con una edificio residencial. Supongamos que el edificio existen 100 apartamentos, con identificación y dentro de esos apartamentos viven cierta cantidad de personas. Pues digamos entonces que el edificio es la memoria, el cual tiene 100 bloques de almacenamiento (los apartamentos) identificados por una dirección (nro del apartamento) y dentro de cada bloque esta almacenado un dato (cantidad de personas que viven en el apartamento), así si requiero saber el dato (cantidad de personas) de la dirección 50 (nro de apartamento 50), obtendré la información solicitada (número de personas que residen en ese apto). 

Gráficamente se puede representar así: 

Fíjense que en la dirección 0001 la memoria tiene almacenado el dato BB y en la 0002 el dato AA. Ahora, ¿Es necesario conocerse las direcciones? Pues no, lo único que debemos saber es que podemos apuntar a una dirección que almacena un valor de una variable y de allí poder explorar la memoria a nuestro antojo.

Punteros

Lo anterior es una breve descripción de la memoria y su direccionamiento como conocimiento previo para poder entender un poco el tema de los punteros. Al inicio ya se dijo que era un puntero. Ahora veamos su aplicación práctica. Comencemos con su sintaxis de inicialización:

[tipo de dato] *nombre_del_puntero.

#include <stdlib.h>
#include <iostream>
using namespace std;

int main () {
   int *p1;
   float *p2;
   char *p3;
}

Se puede notar que la sintáxis es similar a la declaración de una variable con la salvedad que al nombre del puntero lo antecede el operador asterisco (*). A su vez se puede observar que se puede crear un puntero con cualquiera de los tipos de datos conocidos en C++. 

Para concluir estudiemos el siguiente código:

#include <stdlib.h>
#include <iostream>
using namespace std;

int main () {
    int num1, num2;
    int *p1, *p2;

    p1 = &num1;
    p2 = &num2;

    num1 = 10;
    num2 = 20;

    cout << "De esta forma muestro la dirección a las que apuntan p1 y p2" << endl;
    cout << "p1 = " << p1 << " p2 = " << p2 << endl;
    cout << "De esta forma muestro los datos que almacenan sus direcciones:" << endl;
    cout << *p1 << " - " << *p2;

    p1 = p2;
    p2 = 0;

    return 0
}

Bien, analicemos el ejercicio:
  • Se crean dos variables num1 y num2 tipo entero (int) y dos punteros p1 y p2 también tipo entero ya que son creados con la finalidad de apuntar a las direcciones de las variables. Es importante que los punteros y variables que se vayan a relacionar tenga el mismo tipo de dato.
  • Luego se asigna la dirección a la que apunta la variable num1 al puntero p1; lo mismo ocurre con la variable num2 y el puntero p2. Nótese que para indicar que un puntero apuntará a la dirección de una variable se debe anteceder al nombre de la variable con el operador AND (&), si esto se obvia el depurador dará un error. A pesar que las variable no tengan algún valor asignado, éstas ya tienen una dirección de ubicación en la memoria, es por eso que se le puede pasar esa información a los punteros.
  • Luego se le asigna un valor a num1 y num2, lo cual de manera directa permite que se pueda obtener ese valor desde el puntero.
  • Luego se imprime en pantalla dos informaciones. Primero se muestra la dirección a la que apuntan los punteros p1 y p2; que en la práctica es el lugar de la memoria donde están almacenado los datos de las variables num1 y num2. Esto lo notamos cuando se imprime el segundo bloque de texto, en la que se imprime los datos a los que apuntan p1 y p2, colocando como prefijo el asterisco en cada puntero. 
  • Como remate le paso la dirección de p2 a p1, y posteriormente le digo que p2 que apunte a la dirección 0 de la memoria.
Este ejemplo no trata de cumplir con un objetivo en particular, sino de mostrarles como se opera con punteros.
Espero les haya gustado. Nos vemos.

domingo, 20 de mayo de 2018

Apéndice: Cadena de Caracteres


Al inicio de este curso se expuso los tipos de datos más usados en la Programación en C++ (ver Capítulo: Tipo de Datos, Variables y Constantes), siendo el char el tipo que define a una variable que almacenará caracteres. La limitante de una variable tipo char es que sólo puede almacenar un caracter; si deseábamos guardar una cadena de texto, con una variable tipo char no era posible. Entonces cómo hacerlo, pues es lo que veremos a continuación.

Las cadenas de caracteres se manejan como arreglos y existen dos maneras de declararlas. Si trabajamos con la version de C se declara un arreglo tipo char sin tamaño. Con la versión de C++ se agrega otro tipo de datos propio para el manejo de cadena de caracteres (string). Ambas declaraciones crean un arreglo en la que cada posición del mismo contiene un caracter del texto declarado. Veamos un ejemplo del mismo.

#include <stdlib.h>
#include <iostream>
using namespace std;

int main (){

    char saludo[] = "Hola";
    string bienvenida = "Estas en tratado tecnologico";
    cout << saludo << ". " << bienvenida << endl;
    cout << saludo[0] << bienvenida[9];

    return 0;
}

Si nos fijamos en el ejercicio anterior podemos notar lo siguiente:
  • Se muestran dos maneras de declarar una arreglo: 
    • En la primera se declara un arreglo (saludo[]) tipo char sin indicar el tamaño del mismo. El tamaño del arreglo lo asigna el compilador una vez contabilice la cantidad de caracteres que tenga la cadena más un espacio el cual se asigna como indicador del fin de texto.
    • La segunda forma se crea una variable (bienvenida) tipo string, el cual será tratado como un arreglo si se desea explorar cada carácter del texto.
    • Aunque es posible crearlo con el tipo string, mi recomendación es crear un arreglo tipo char, ya que las funciones para el tratamiento de cadenas de caracteres indican un error en el tipo de datos cuando la cadena es creada tipo string.
  • A pesar de ser declarado como un arreglo, para mostrar sus valores no hace falta explorarlo con un for, solo es necesario colocar el nombre del arreglo.
  • También se puede observar que si deseamos mostrar un carácter en específico podemos hacer mención al mismo colocando el índice en el arreglo. Por ejemplo: saludo[0] muestra la H y bienvenida[9] imprime T.
Una de las preocupaciones de toda persona que se inicia en la programación y estudia los tipos de datos es cómo tratar las cadenas de caracteres, ya que al inicio sólo se le indica el tipo char el cual permite manipular un sólo carácter. Pues bien acabamos de dar la respuesta. Esto no se atiende al inicio debido que es necesario conocer la estructura de los arreglos para poder comprenderlo con mayor facilidad.

Ahora, hagamos un programa que permita contabilizar la cantidad de caracteres que tiene una cadena de texto. Para esto le mostraré dos formas de hacerlo.
Versión 1:   
#include <stdlib.h>
#include <iostream>
using namespace std;

int tam_cadena_char( char cadena[] );
int tam_cadena_string( string cadena );

int main (){

    int cant_sal, cant_bien;

    char saludo[] = "Hola";
    string bienvenida = "Estas en Tratado Tecnologico";
    cout << saludo << ". " << bienvenida << endl;
    cout << saludo[0] << bienvenida[9];
    cout << tam_cadena_char(saludo) << endl;
    cout << tam_cadena_string(bienvenida);
    return 0;
}
int tam_cadena_char( char cadena[] ){

    int i = 0;
    while (cadena[i] != 0){
        i++;
    }
    return i;
}
int tam_cadena_string( string cadena ){

    int i = 0;
    while (cadena[i] != 0){
        i++;
    }
    return i;
}


Versión 2: 
#include <stdlib.h>
#include <string.h<
#include <iostream>
using namespace std;

int main (){

    char saludo[] = "Hola";
    cout << saludo << ". " << endl;
    cout << saludo[0];
    cout << strlen(saludo) << endl;
    return 0;
}  

Se puede notar en ambas versiones que el programa cumple con lo requerido, pero veámoslo con detalles:
  • En la Versión 1:  Se crean dos funciones para contabilizar la cantidad de caracteres una para las cadenas creadas tipo char y otro para los tipo string. A estas funciones se les pasa como argumento los arreglos y dentro se ejecuta un while cuya condición de ruptura es cuando dentro de la cadena se consigue un valor 0. Este valor se coloca al final de cada cadena para indicar su final. Posteriormente esa función retorna la cantidad de caracteres la cual ha sido almacenada en la variable i.
  • En la Versión 2: Se agrega una nueva librearía string.h la cual nos permite acceder a una cantidad de funciones propias para la manipulación de cadena de caracteres. Es así como se utiliza la función strlen pasándole como parámetro el arreglo saludo. Esta función devuelve como valor el tamaño del carácter.
Los invito a probar estos programas y estudiar su funcionamiento. Así como al agregar la librería string.h se facilitó la manipulación de funciones, existen otras librerías que condensan una serie de funciones que apoyan otras tareas en la programación en C++. En el Apéndice: Librerías se ofrecen algunas de ellas.

Apéndice: Librerías


Cuando programamos en cualquier lenguaje de programación a veces nos preguntamos cómo es que el compilador entiende o interpreta lo que esta escrito. Pues existen funciones cuya tarea es convertir lo  escrito en lenguaje máquina para que el procesador se encargue de ejecutarlo. También  hay funciones que utilizamos como el cin y cout que no hemos definido y aun así todo funciona. Entonces ¿Cómo es que podemos utilizarlos?

Al inicio de cada programa se escriben unas líneas de instrucciones que corresponden a una biblioteca de funciones condensadas en un archivo, comúnmente llamadas librerías. Es así como es posible utilizar funciones básicas para hacer nuestros programas. Dependiendo de la complejidad y particularidad de nuestros proyectos se podrán agregar más librerías para tener acceso a funciones más específicas.

Ejemplo de la declaración de la librerías:

#include <stdlib.h>
#include <iostream>
using namespace std;

La instrucción using namespace std nos ayuda a declarar un espacio de nombre que evita tener que usarlo cada que accedemos a alguna función especifica de una librería. Teniendo este namespace declarado podemos llamar por ejemplo el comando cout >>, que pertenece a la librería iostream, sin embargo sin este namespace sería std::cout >>, imagina tener que hacer esto cada vez que uses algún comando o función de las librerías, sería bastante tedioso.

Hay que acotar que las funciones definidas en las librerías nos facilitan a realizar ciertas tareas en nuestros programas; sin embargo habrá momentos en la que dentro de las mismas no haya una función que haga lo que necesitamos, en este caso nos tocará crearla nosotros.

Algunas de las librerías más comunes en C++ son:
  • fstream: Flujos hacia/desde ficheros. Permite la manipulación de archivos desde el programar, tanto leer como escribir en ellos.
  • iosfwd: Contiene declaraciones adelantadas de todas las plantillas de flujos y sus typedefs estándar. Por ejemplo ostream.
  • iostream: Parte del a STL que contiene los algoritmos estándar, es quizá la más usada e importante (aunque no indispensable).
  • La biblioteca list: Parte de la STL relativa a contenedores tipo list; listas doblemente enlazadas
  • math: Contiene los prototipos de las funciones y otras definiciones para el uso y manipulación de funciones matemáticas.
  • memory: Utilidades relativas a la gestión de memoria, incluyendo asignadores y punteros inteligentes (auto_ptr).
    "auto_ptr" es una clase que conforma la librería memory y permite un fácil manejo de punteros y su destrucción automaticamente.
  • Biblioteca new: Manejo de memoria dinámica
  • numeric: Parte de la librería numérica de la STL relativa a operaciones numéricas.
  • ostream: Algoritmos estándar para los flujos de salida.
  • queue: Parte de la STL relativa a contenedores tipo queue (colas de objetos).
  • Librería stdio: Contiene los prototipos de las funciones, macros, y tipos para manipular datos de entrada y salida.
  • Librería stdlib: Contiene los prototipos de las funciones, macros, y tipos para utilidades de uso general.
  • string: Parte de la STL relativa a contenedores tipo string; una generalización de las cadenas alfanuméricas para albergar cadenas de objetos. Muy útil para el fácil uso de las cadenas de caracteres, pues elimina muchas d elas dificultades que generan los char
  • typeinfo: Mecanismo de identificación de tipos en tiempo de ejecución
  • vector: Parte de la STL relativa a los contenedores tipo vector; una generalización de las matrices unidimensionales C/C++
  • forward_list Esta librería es útil para implementar con gran facilidad listas enlazadas simples.
  • list Permite implementar listas doblemente enlzadas (listas enlazadas dobles) facilmente.
  • iterator Proporciona un conjunto de clases para iterar elementos.
  • regex Proporciona fácil acceso al uso de expresiones regulares para la comparación de patrones.
  • thread Útil para trabajar programación multihilos y crear múltiples hilos en nuestra aplicación.

Las librerías presentadas anteriormente son estándar para C++; pero posiblemente podrás ubicar por la red alguna otras creadas por desarrolladores que se adapten a tus requerimientos.

Para concluir. Es muy difícil crear un programa sin usar librerías. En ellas podremos encontrar funciones que nos facilitan mucho algunas tareas. Un ejemplo de ellos lo podemos notar en uno de los ejercicios presentados en el Apéndice: Cadena de Caracteres. Las librerías las utilizaremos según nuestra conveniencia. Recomiendo no agregar librerías por hacerlo, sino porque realmente se necesite, ya que cada biblioteca que se añada carga de información a nuestro programa haciéndolo más grande y pesado.

 

jueves, 17 de mayo de 2018

Funciones II: Paso de Parámetros


En el capítulo anterior iniciamos el contenido de Funciones, estableciendo la forma que éstas de declaran y define. A su vez, se ofreció 2 ejemplos sencillos, quedando por tratar el tema de paso de parámetros hacia una función. 

¿Qué significa pasar parámetros? Pues revisemos; en los ejercicios anteriores se declaraba y definía una función para luego ser llamada dentro de la función principal main (Sí, main() es un función conocida como función principal debido que es la que se ejecutará al correr el programa). Esta función que era llamada no recibía algún valor, sino que manejaba sus variables locales y ejecutaba las instrucciones que les fueron definida. Lo que se propone con el paso de parámetros es que se le pueda pasar valores a la función para que trabaje con ellas.

Para definir una función que recibirá parámetros se debe agregar ésta característica a la sintaxis, quedando de la siguiente forma:

[tipo de dato] nombre_función ( [tipo de dato] variable1,... [tipo de datos] variable n);

Tal vez lo veas un poco confuso, pero hagamos un ejercicio para detallarlo mejor. El ejercicio que haremos es el del capítulo anterior donde determinábamos los números pares de un rango de valores, donde crearemos una función que evalúe esos valores.

#include <stdlib.h>
#include <iostream>
using namespace std;

int mensaje();
int evaluacion(int num_min, int num_max);

int main(){

    int num_min, num_max;
    cout << "Indique el numero minimo del rango ";
    cin >> num_min;
    cout << "Indique el numero maximo del rango ";
    cin >> num_max;

    evaluacion(num_min, num_max);

    return 0;
}
int mensaje (){
    cout << "Este numero es par";
    return 0;
}
int evaluacion(int num_min, int num_max){
     for (int i = num_min; i <= num_max; i++){
        if ( i % 2 == 0){
            cout << i << " - ";
            mensaje();
            cout << endl;
        }
    }
}

Fíjense como quedó. Analicemos el ejercicio para comprenderlo mejor:
  • Se declaran dos funciones mensaje() para mostrar por pantalla el texto "Este número es par"  y evaluación() para hacer la evaluación de los números. Estas mismas funciones se definen después del main() estableciendo qué hará cada una de ellas.
  • La función evaluacion() se declara para que reciba dos parámetros los cuales colocamos dentro de los paréntesis. Dentro de los paréntesis seguimos la sintáxis descrita anteriormente: Se dice que tipo de datos se pasarán a la función (en este caso ambos son enteros int) y se indica el nombre de la variable el cual se asociará con las variables locales de la función. Nota: No es necesario que las variables a pasar tenga el mismo nombre de las variables que se manejan en el main(), en esta oportunidad les coloqué el mismo nombre para no confundirnos.
  • En el main(), después de capturar por teclado los valores de num_min y num_max, se llama a la función evaluacion(num_min, num_max) pasándole los datos requeridos.
  • La función evaluacion() de ejecuta y dentro, a su vez, llama a la función mensaje().
Ahora veamos algo, para aquellos estudiantes que entregan el mismo programa argumentando que se reunieron para hacerlo. Entiendo que se reunan para discutir el ejercicio, pero la lógica de cada persona permite obtener el mismo resultado aplicando diferente caminos. Voy hacer el mismo ejercicio de otra manera y verán que se logrará el mismo resultado:
#include <stdlib.h>
#include <iostream>
using namespace std;

int mensaje();
int evaluacion( int numero);

int main(){

    int num_min, num_max;
    cout << "Indique el numero minimo del rango ";
    cin >> num_min;
    cout << "Indique el numero maximo del rango ";
    cin >> num_max;
     for (int i = num_min; i <= num_max; i++){
        evaluacion(i);
    }
    return 0;
}
int mensaje (){
    cout << "Este numero es par";
    return 0;
}
int evaluacion( int numero){
    if ( numero % 2 == 0){
        cout << numero << " - ";
        mensaje();
        cout << endl;
    }
}

Como pueden notar se obtiene el mismo resultado con un programa cuya función evaluación ha sido modificada. ¿Cómo fue modificada? De la siguiente manera:
  • La función evaluacion(int numero) ha sido declarada para recibir un solo valor como parámetro. Y en este caso será el número a evaluar.
  • El for queda dentro de la función main() y en cada iteración se llama a la función evaluacion() pasándole la variable i como parámetro. Nótese que el nombre de la variable de la función evaluacion(numero) y la que se pasa al llamar la función (i) no coinciden en nombre. Esto se hizo intencional, ya que como les comente no es necesario que estas tengan el mismo nombre. Pero si es necesario que dentro de la función nombre coincida.
  • Lo anterior se evidencia al ver en la función evaluacion() cuando en la estructura de control if se revisa la condición, allí se realiza la división con residuo con la variable numero.
Como pueden ver se ha presentado dos lógicas para atender el mismo problema. Decir cual es mejor depende de cuánto tiempo y recursos utilizan para ejecutarse. Pero se evidencia que si se puede hacer.

Quiero acotar que las variables que se definen dentro de la función pueden ser de cualquiera de los tipos de datos vistos en el curso. Para nuestro ejercicio se declararon int porque los datos que se pasarán serán enteros.
Con esto se concluye el capítulo de funciones. Espero lo hayan disfruta. Ya estamos llegando al final de este curso. Quedán tratar los temas de punteros y estructuras.
¡Nos vemos!

miércoles, 16 de mayo de 2018

Funciones I: Declaración y Definición





Hola estimados cursantes, hemos llegado a uno de los capítulos más relevantes de este curso y de la programación en general: Funciones. Hasta los momentos se han desarrollados programas que tratan de cumplir con un objetivo o propósito; sin embargo sólo con las herramientas ofrecidas es muy poco probable que se creen aplicaciones complejas óptimas, entendiéndose como óptimo que sean eficiente: usar la menor cantidad de recursos para lograr el objetivo; y eficaz: que la meta se logre en el menor tiempo posible.

Que esto no suene desalentador porque como les he mostrado sí hemos creado programas funcionales, pero ahora vamos a tratar de optimizarlos un poco más. ¿Cómo lograremos esto? Aplicando los conceptos de funciones.

Supongamos que tenemos un programa con 1.000 líneas de código y detectamos dentro de ese código que hay un bloque de instrucciones que se repite en variadas ocasiones en distintas partes del programa. También observamos que si logramos separar ese bloque de instrucciones y lo centralizamos en una zona del programa podemos reducir la cantidad de código del mismo en 800 líneas. ¿Cómo podríamos hacer eso? Pues, la respuesta es con Funciones.

Una función es una estructura que permite agrupar unas instrucciones de nuestro programa la cual ha de estar disponible para ser ejecutado cada vez que es solicitado y que, comúnmente, retorna un valor. Normalmente la lógica que envuelve este segmento código se repite durante todo programa y para evitar su repetición se separa en una función. Veamos cómo trabaja.

Toda función pasa por dos procesos: La declaración y su definición. En la declaración indicamos que tipo de datos ha de retornar y si recibe algún valor como referencia. En la definición se colocan las instrucción que se ejecutarán al ser llamada la función.

La sintaxis para la declaración es:

[tipo de dato] nombre_función ();

y la sintaxis para su definición: 

[tipo de dato] nombre_función (){
   instruccion_1;
   ...
   return [valor, variable];
}

Veamos un ejemplo: 

#include <stdlib.h>
#include <iostream>
using namespace std;

float suma (){

    float valor,num1,num2;
    cout << "Introduzca el numero 1 ";
    cin >> num1;
    cout << "Introduzca el numero 2 ";
    cin >> num2;
    valor = num1 + num2;
    return valor;
}

int main (){
    float resultado;
    resultado = suma ();
    cout << "El resultado es " << resultado;

   return 0;
} 


Analicemos lo siguiente:
  • Se declara y se define la función suma() antes del main(), esto es requerido debido que dentro del main() llamo a la función suma() y si no la he declarado antes dará un error debido que no la encuentra. Recuerden que las instrucciones se ejecutan de forma lineal, entonces no se puede llamar a una función que se desconoce. Por eso se declara y más adelante se hace el llamado. 
  • Se aprovecha la oportunidad para definirla (indicar que instrucciones se ejecutarán). Lo común es que se declare antes del main() y se defina después del main() tal como se verá en el próximo ejemplo. 
  • En la función suma() se declaran las variables num1, num2 y valor. Estas variables son locales de esa función, es decir, solo almacenarán datos y podrá ser manipuladas únicamente desde la función que las declaró, en este caso suma(). Además notemos que la función retorna lo que almacena la variable valor
  • Posteriormente, dentro del main() se declara una variable resultado que recibirá el valor que retornará la función suma(), la cual se llama. No siempre es necesario capturar algún valor de retorno de la función; a veces sólo es requerido que se ejecute algunas instrucciones y ya. 
  • La forma de llamar una función es colocando su nombre, los paréntesis y si recibe parámetros se debe colocar; este no es el caso: Más adelante haremos ejemplos con funciones que reciben parámetros.
Para culminar este capítulo hagamos un ejercicio: Hacer un programa que obtenga los números pares de un rango valores para lo cual se solicita al usuario el inicio y fin de ese rango; mostrando en pantalla los números pares con un mensaje indicando "Este número es par"; la muestra del mensaje se debe hacer mediante una llamada a una función.
#include <stdlib.h>
#include <iostream>
using namespace std;

int mensaje();

int main (){
    int num_min, num_max;
    cout << "Indique el numero minimo del rango ";
    cin >> num_min;
    cout << "Indique el numero maximo del rango ";
    cin >> num_max;

    for (int i = num_min; i <= num_max; i++){
        if ( i % 2 == 0){
            cout << i << " - ";
            mensaje();
            cout << endl;
        }
    }

   return 0;
} 

int mensaje (){
    cout << "Este numero es par";
    return 0;
}


Veamos este ejemplo:
  • A diferencia del primer ejercicio acá de declara la función antes del main() y se define después de él. En la práctica es muy común hacerlo de esta forma. Aunque de ambas maneras es correcto.
  • Se solicita al usuario que indique el valor mínimo y máximo del rango y se recorre cada uno de esos valores con un for inicializando la variables i con num_min hasta que sea mayor a num_max.
  • Con if evaluamos i % 2 == 0, esta operación es una división, pero el resultado obtenido es el residuo de la división. Me explico, al dividir 5 entre 2 obtenemos como resultado 2 con residuo = 1; esto ocurre con todos los números impares; por el contrario con los números pares el residuo siempre será 0. Razón por lo cual comparo el residuo de la operación con 0, si es igual muestro el número con su mensaje. Esta es una técnica para saber si un número es par o impar.
  • Para mostrar el mensaje notemos que primero imprimimos el número, luego llamamos la función mensaje el cual muestra el texto "Este número es par" y posteriormente bajo una línea para darle una buena apariencia. 

Espero que con estos dos ejemplos hayan comprendido el funcionamiento de una función. En el próximo capítulo ahondaremos en ello ya que ahora es que se le puede sacar provecho a las funciones.