domingo, 20 de mayo de 2018

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.

miércoles, 9 de mayo de 2018

Array II: Arreglos Multidimensionales


Hola estimados lectores ¿Qué les ha parecido el curso? Si tienen oportunidad comenten acerca de su experiencia con é, así como también si tienen alguna duda. Con gusto les apoyaré. Ahora bien, continuemos con el siguiente capítulo. En el artículo anterior se dio inicio a las estructuras de arreglos con el tema Array I: Arreglos Unidimensionales, se le ha llamado unidimensionales precisamente por eso: Se expanden en una sóla dimensión, es decir, si indicamos el tamaño del arreglo ésta tendrá n cantidad de posiciones dirigidos en una misma dirección.

Todo esta bien, existen muchas cosas que pueden modelarse mediante un simple arreglo, pero en ocasiones requeriremos más de una dimensión para almacenar datos con características similares; en estos casos usaremos arreglos de más de una dimensión: Arreglos Multidimensionales.

Arreglos Multidimensionales

Cuando hablamos de arreglos multimendionales se hace referencia a cualquier array cuya dimensión es mayor a uno. Sin embargo, para fines prácticos y por estar más cercano a ejemplos cotidianos, estudiaremos arreglos de dos dimensiones, los cuales también pueden ser llamados Vectores.

Los vectores son arreglos de dos dimensiones (bidimensionales) cuya referencia a las posiciones se hacen a través de una dirección horizontal (columnas) y otra vertical (filas). Su sintaxis es la siguiente:

[tipo de dato] nombre_del_arreglo [tamaño_fila][tamaño_columna] (= {dato1, dato2,...,datoNxM} valor (opcional));

La sintaxis es similar al arreglo unidimensional con la salvedad que se debe indicar no sólo un tamaño sino dos: Uno para la fila y otro para la columna; adicionalmente si se inicializará con valores algunos valores estos se deben colocar unos tras otro (separados por coma). Un vector se representa gráficamente así:

Para asignar o extraer una datos dentro de un vector es necesario indicar tanto su fila como su columna. De esta maneras nos ubicamos en la posición deseada y manipulamos el dato. Es mejor que veamos un caso para entender mejor esta estructura:

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

int main (){
   float vector [2][2] = {2, 3, 4, 5};
   vector[0][1] = 5;

   for (int i = 0; i < 2; i++){
      for (int j = 0; j < 2; j++){
         cout << "-" << vector [i][j];
      }
   }

   return 0;
} 

En el ejemplo anterior vemos los siguiente:
  • Declaración del vector con valores iniciales: Se ha creado una vector 2x2, cuyos valores iniciales son los descritos entre llaves.
  • Luego modifica un dato en la posición de la fila 0 y columna 1 vector[0][1] = 5
  • Para recorrer por completo un vector utilizaremos la estructura de control for anidado. Vean que encontramos dos for, el primero utiliza como índice la variable i y el más interno la variable j. El funcionamiento es el siguiente, primero se ejecuta en el primer for y se verifica si la condición se cumple; para la primera vez la variable i es igual a cero (i=0), por lo que sí se cumple, permitiendo que se ejecute el segundo for. Acá también se evalúa la condición de j < 2; lo que hará que se recorra el vector dejando fijo la fila en 0 y moviendo la columna. Una vez que se recorra por completo toda la columna se incrementa la fila (variable i) y la variable j es reinicializada en cero. Así ocurrirá hasta que se haya paseado por todo el vector.
  • Para este caso se recorre todo el vector para mostrar sus valores.
Creo que ahora podemos agregarle un poco de dificultad a esto (aunque no considero sea difícil si le agarras el ritmo). El enunciando es el siguiente: Realizar un programa que cree una matriz cuadrada de un tamaño definido por el usuario. Le solicite al mismo los valores que rellenará la matriz y por último se muestre los valores de la diagonal principal.
#include <stdlib.h>
#include <iostream>
using namespace std;

int main (){
   int tam;

   cout << "Ingrese el tamaña del vector: ";
   cin >> tam;
   float vector [tam][tam];

   //Insercción de los Datos
   for (int i = 0; i < tam; i++){
      for (int j = 0; j < tam; j++){
         cout<<"Ingrese un dato para la posición "<<i<<","<<j<<": ";
         cin >> vector [i][j];
      }
   }
   cout << "Los valores de la Diagonal Principal de la matriz es "<<endl;
   for (int i = 0; i < tam; i++){
      for (int j = 0; j < tam; j++){
         if (i == j){
            cout <<" | "<< vector[i][j];
         }
      }
   }

   return 0;
}

¿Les pareció complicado el ejercicio? Fíjense que aplicando un poco de lógica y lo que hemos visto hasta ahora los resolvemos. Qué tal si lo analizamos:
  • El ejercicio le pide al usuario que indique el tamaño de una matriz cuadrada, esto significa que tanto su fila como columna es del mismo tamaña; por ese motivo se le solicita la usuario que indique un solo dato para el tamaño, cuyo valor es guardado en la variable tam.
  • Esa variable es usada para declarar al array vector[tam][tam]. Se declaró float para dar libertad a ingresar números con decimales.
  • Luego se colocan los for anidados para solicitar al usuario los datos de serán asignados al vector.
  • Más adelante se utiliza la misma estructura de for anidados para recorrer el arreglo. Dentro de  esta estructura se insertó un if, esto es necesario para que se imprima solo los valores de la Diagonal Principal. La Diagonal de una matriz es aquella donde sus índices son iguales; esto es: Los índices de la fila y columna son iguales (i==j). Es por eso que con el if se condiciona que cuando esto ocurra se imprima el valor del vector.
Con respecto a vectores se pueden aplicar muchos otros ejercicios interesantes. Les aseguro que haré un Apéndice con varios ejercicios hechos. Ya damos por concluido este capítulo de arreglos, pero antes les quiero dejar las siguientes observaciones:
  • A pesar de haber estudiado como Arreglos Multidimensionales con vectores bidimensionales, es posible crear arreglos de multidimensiones. Hasta ahora no he conseguido límites para ello, claro siempre he creado arreglos cuyos dimensiones son razonables. Recuerda que las variables y arreglos utilizan tu memoria RAM como espacio de almacenamiento y éste componente físico sí es limitado.
  • Es casi como una fórmula fija que para recorrer un arreglo completo utilizar un for anidado. El nivel de anidación dependerá de la dimensión del arreglo. Es por ello que si tenemos un arreglo de una dimensión usamos una sola estructura for. Si es bidimensional utilizamos dos for y así sucesivamente.
  • Recomiendo utilizar siempre for para recorrer un arreglo. Note que se puede ajustar una estructura while para recorrer un arreglo. Pero lo más práctico es usar el for.
Espero les hayan gustado este capítulo. Nos vemos en la próxima.

domingo, 6 de mayo de 2018

Array I: Unidimensionales


Hola estimados lectores y seguidores de este curso básico de Programación; antes de dar inicio al tema que atenderá este capítulo estudiemos la siguiente situación: Supongamos que hacemos un programa que se encarga de solicitar las notas de un estudiante universitario durante un semestre para almacenarlas y luego obtener su promedio en ese período académico. Agreguemos, además, que la cantidad de cátedras cursadas por los estudiantes son las mismas (cuestión que sabemos, en la práctica, no es necesariamente cierto).
 
Lo primero que se nos ocurre es crear las variables; aquí nos encontramos que debemos crear una cierta cantidad de ellas, pero cuántas el enunciado no nos lo dice. Para no complicarnos tanto al principio digamos que son 7 calificaciones. Esto nos obliga a crear la misma cantidad de variables para solicitar la información al usuario, guardarlas y luego manipularlas con el objeto de obtener el promedio. Perfecto, hecho.

Ahora extendamos esa suposición a algo más real. Tenemos el mismo programa que realizar, pero ahora no conocemos cuantas calificaciones ingresará el usuario y en vez del registro de un semestre se requiere de todas las cátedras que ha cursado durante la carrera; lo que nos obliga a crear n cantidad de variables (las cuáles no sabemos).

Si de por sí tener 7 variables de la mismas característica (notas) es impráctico y engorroso, lo es más si se deben crear 100 -y quizás me quede corto- para almacenar una cantidad de datos que desconocemos (imagínense como quedará la declaración de variables de este programa).

Arreglos Unidimensionales

Para estos casos existe una estructura de datos se ajusta perfectamente, estos son los Arreglos (Arrays en inglés). Los arreglos son estructuras de almancenamiento de datos similares a las variables que permiten reunir en ella varios datos de la mismas características. Por ejemplo, en el caso anterior que tenemos 7 calificaciones, en vez de crear siete variables (posiblemente nota1, nota2, ..., nota7) crearemos un arreglo llamado notas con siete posiciones de memorias. ¿Esto parece confuso? Veamos gráficamente su estructura para comprenderlo mejor.


 En la imagen anterior tenemos la estructura de un arreglo, cuyas particularidades son:
  • Es una Arreglo cuyo tamaño es 5, es decir, tiene 5 posiciones de memorias para almacenar esa cantidad de datos.
  • Poseen unos índice que sirven para ubicarnos dentro del arreglo y manipular un dato en específico.
  • En C++ los índices inician en 0, por eso razón es que vemos que para un arreglo de tamaño 5 los índices van de 0 a 4. Esto es muy importante tenerlo presente ya que si lo obviamos más adelante puede darnos un dolor de cabeza al momento de recorrer dichos arreglos.
Si lo vemos de otra forma, esta estructura es como cuando hacemos una cola para comprar. Todos tenemos una posición (índices) y en cada posición hay una persona cuya información es diferente (dato), pero el objeto a evaluar son lo mismo: personas (a menos que pongas a tu mascota hacer cola).

Bien, ya conocemos su estructura, ahora cómo la usamos. La forma de declarar un arreglo es de la siguiente forma:

[tipo de dato] nombre_del_arreglo [tamaño del arreglo] (= {dato1, dato2,...,datoN} valor (opcional));

Estudiemos esta sintáxis: 
  • Al igual que las variables, en los arreglos hay que definir el tipo de dato (int, float, char) y el identificador que tendrá (nombre del arreglo).
  • Se debe indicar el tamaño del arreglo, esto es, decir cuántos datos almacenará. En C++ es obligatorio que al momento de declarar el arreglo se indique su tamaño. En otros lenguajes como Python se puede dejar en blanco y luego se le dice que tamaño será. 
  • Como opcional se puede inicializar la variable indicando entre llaves { } los datos que tendrá. Lo común es que no se inicialice y carguen los datos con valores que se generan dentro del programa.
Ahora creo que estamos listo para ver un pequeño ejemplo:
 
#include <stdlib.h>
#include <iostream>
using namespace std;

int main (){
   int arreglo [3] = {1, 8, 10};

   arreglo [2] = 4;

   cout << "Ingrese un numero para modificar el valor del arreglo en la posicion 0";
   cin >> arreglo[0];
   cout << "El valor del arreglo en la posicion 0 es: " << arreglo [0];

   return 0;
} 

Analicemos este ejemplo:
  • Se crea un arreglo tipo entero de tres posiciones y de inicializa con tres valores. Fíjense que la sintaxis corresponde a la propuesta de arriba.
  • En la siguiente línea se cambia el valor del arreglo en la posición 2, es decir en valor 10 cambia por 4. Esta es la manera en la que se asigna un dato.
  • En las dos siguientes instrucciones se muestra un texto solicitando un nuevo dato para el arreglo en la posición 0 y con cin se captura por teclado y asigna a dicho arreglo en la posición 0 (cin >>arreglo [0]).
  • Posteriormente se muestra dato del arreglo en la posición 0.
En el ejemplo anterior vimos, inicialización de variable con valores iniciales; inserción de datos en un arreglo, tanto por un valor asignado como por captura por teclado, y muestra de un dato. Fíjense también que para indicar la posición en un arreglo colocamos un número que corresponde al índice. También es posible indicar la posición por asociación, esto es indicar a través de una variable el índice. Veamos de qué hablo.
#include <stdlib.h>
#include <iostream>
using namespace std;

int main (){
   int arreglo [3] = {1, 8, 10};
   int i = 2; j = 0;

   arreglo [i] = 4;

   cout << "Ingrese un numero para modificar el valor del arreglo en la posicion 0";
   cin >> arreglo[j];
   cout << "El valor del arreglo en la posicion 0 es: " << arreglo [j];

   return 0;
} 

Este ejercicio es similar al anterior, con la diferencia que ahora se indica la posición de los índices mediante la asociación de las variables i y j. Listo, qué les parece si complicamos un poco más los ejercicios para esta vez utilizar la sentencia de repetición for en el recorrido de los arreglos.

Propongo hacer el programa del enunciado con la cual iniciamos este artículo. Vamos a solicitar al usuario las n cantidad de calificaciones y obtener su promedio. Previamente se le debe pedir al estudiante que nos indique cuántas notas ingresará. Iniciemos:

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

int main (){
   int tam, suma = 0, prom;

   cout << "Indique la cantidad de notas que ingresará ";
   cin >> tam;

   int notas [tam];

   //Carga de Datos
   for (int i = 0; i < tam; i++){
      cout << "Ingrese una calificación :";
      cin >> notas[i];
   }
   //Calculo del Promedio
   for (int i = 0; i < tam; i++){
      suma = suma + notas[i];
   }
   prom = suma / tam;
   cout << "Tu promedio es: " << prom;

   return 0;
} 

Veamos:
  • En esta ocasión antes de crear el arreglo inicializamos tres variables tam, suma y prom. La variable tam la usaremos para capturar la cantidad de notas que ingresará el estudiante, suma para almacenar la sumatoria de la nota y prom para el promedio.
  • Una vez que el usuario indica cuantas calificaciones ingresará se crea el arreglo a través de la asociación con la variable tam
  • Posteriormente se utiliza la estructura de repertición for para recorrer el arreglo y asignar las notas que el usuario ingresará por teclado. En el for indicamos la variable que será de cambio la i la que, a su vez, será la asociación del índice del arreglo; por ejemplo, cuando i = 0, se apuntará al arreglo en la posición 0 (notas[i]). En el mismo for se dice que se repetirá la acción mientras la condición de i < tam se cumpla y luego i aumentará en uno (i++) al llegar al final del for.
  • Una configuración similar del for se utiliza para obtener el promedio. Se recorre el arreglo y en cada posición se realiza una sumatoria (suma = suma + notas[i]). Nota: Es importante que para las sumatoria se inicialice la variable que acumulará el dato en 0. En este caso, la variable suma se inicializó así suma = 0.
  • Por último, fuera del for, se obtiene el promedio y se muestra el dato.
Este ejercicio, apesar de ser sencillo, es suficiente para entender cómo funcionan los arreglos y cómo se recorren para manipular sus datos. Es requerido que lo analicen y si tienen algún inconveniente no duden en comentar.
Con esto concluimos esta primera parte de arreglos. Como su título lo indica son arreglos unidimensionales, es decir de una dimensión. En el siguiente capítulo estudiaremos los arreglos multidimensionales, también conocidos como vectores.

martes, 1 de mayo de 2018

Estructura de Repetición II: FOR



Continuamos con las Estructuras de Repetición; en esta oportunidad con el FOR. En el capítulo anterior estudiamos el WHILE y DO WHILE. ¿Por qué separar el FOR de las dos anteriores? Considero que, a pesar de ser estructuras usadas para la repetición, la forma y el objeto en la que se usa, son diferentes.

FOR

La sentencia FOR es usado normalmente para recorrer estructura como arreglos (el cual veremos en el capítulo siguiente), cuya características principales son:
  • Usa un mecanismo de iniciación de variable, evaluación de condición y razón de cambio de la variable dentro de una misma línea de instrucción. 
  • La condición que se aplica sobre la variable evalúa que ésta se encuentre dentro de un rango.
  • La variable tiene una razón de cambio fija de incremento o reducción.
Para comprenderlo mejor veamos su sintaxis y ejemplo de su aplicación.

for (iniciacion de variable; condición; razón de cambio){
   instrucciones;
   ...
}

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

int main (){
   int num;
   cout << "Indique de cuál número desea saber su tabla de multiplicación" << endl;
   cin >> num;
   for (int i=0; i < 11; i++) {
      cout << i << "x" << num << " = " << i*num << endl;
   }
   return 0;
} 

He tomado un ejercicio visto en el capítulo anterior por la estructura DO WHILE para que vean la diferencia entre ambas. Quizás comenten que me estoy quedando sin creatividad, esto no es precisamente cierto; lo que ocurre es que quiero que noten el funcionamiento del FOR para, posteriormente ver su potencialidad cuando trabajemos con los arreglos. Ahora, analizando el programa notemos:
  • En una misma línea de instrucción se inicializa la variable, evalúa la condición y se indica la forma que cambiará la variable evaluada, todas estas separadas por punto y coma (;).
  • Lo novedoso, quizá, sea la forma que se presenta la razón de cambio i++ esto indica que la variable i incrementará de uno en uno, lo que sustituye en el caso anterior a i = i + 1. En el caso que se requiere disminuir se utiliza i--.
El FOR es una herramienta muy útil y es muy usada en la exploración de estructuras que contienen índices que indican una posición, tales como los arreglos. De hecho, mientras vayamos agregando mas cursos de programación de nivel avanzado, es muy probables que usemos más esta sentencia de repetición que las otras anteriores.

Con esto concluimos este capítulo, pero antes voy a dejarles unos ejercicios para que practiquen las estructuras de repetición. Estas son las siguientes:
  • Con WHILE: Realice un programa que solicite al usuario un número entre el 1 al 10 repetidamente hasta que ingrese el número 7.
  • Con DO WHILE: Adapte el programa del área de un triángulo visto anteriormente dándole la oportunidad al usuario de elegir si desea repetir la operación.
  • Con FOR: Repita el ejercicio mostrado en este artículo, pero ahora la razón de cambio de la variable i es de dos en dos.
Espero les haya gustado este capítulo. Nos vemos pronto.

Estructura de Repetición I: WHILE Y DO WHILE



En el capítulo anterior agregamos un nuevo valor a nuestros programas: La capacidad de tomar decisión en función a una información que cambia durante el proceso. La estructuras de control nos permiten dirigir el flujo de las instrucciones dependiendo de una condición evaluada.

Aunque esto es funcional -de hecho como vimos anteriormente se logró un programa que cumple un objetivo- aun queda ciertas limitaciones que podemos atender, tal es el caso de si deseáramos repetir cierta tarea o líneas de códigos mientras una condición se cumpla. Para estas situaciones nos valemos de las estructuras de repetición.


WHILE

La primera estructura de repetición que veremos es el WHILE. Para estudiar su funcionamiento comencemos por conocer su sintaxis: 

while (condición){
   instrucciones;
   ...
}

Ahora, comencemos con el análisis de la estructura while:
  • Para iniciar una estructura while primero se ha evaluar una condición y si ésta se cumple se ejecuta el código que se encuentra entre llaves {}.
  • Si queremos que las instrucciones que se encuentren dentro del while se ejecuten, al menos una vez, debemos garantizar que la condición se cumpla, de lo contrario no se ejecutarán.
  • Es indispensable que la variable a evaluar dentro de la condición pueda ser cambiada dentro del código inmerso en el while, ya que si no se hace nunca se romperá el bucle de repetición haciendo que nuestro programa se ejecute infinitamente (claro, al menos que lo cerremos desde el sistema operativo).
  • Al llegar al final de la estructura (cuando se encuentra con el final de la llave }) se retorna al inicio para evaluar nuevamente la condición.
Para comprenderlo mejor hagamos un ejercicio: Se requiere un programa que muestre un mensaje repetitivamente hasta que decidamos no volver a verlo.

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

int main (){
   char opcion = 's';

   while (opcion == 's'){
      cout << "Se estas convirtiendo en un buen programador" << endl;
      cout << "Quieres volver a ver el mensaje? s/n" << endl;
      cin >> opcion;
   }
   return 0;

} 

Veamos con detalle el ejercicio anterior:

  • Se ha creado una variable llamada opcion cuyo valor inicial es el carácter s (en minúscula).
  • En la estructura de repetición while se tiene una condición que evalúa si la variable opcion es igual a s (en minúscula) entonces se ejecutará el código dentro de la estructura y, por ende, se mostrará el mensaje.
  • Posteriormente se muestra un texto indicándole al usuario si desea volver a ver el mensaje confirmándolo ingresado la letra s, o n en caso de no querer volver a verlo.
  • Noten: lo que ingresa el usuario se guarda en la variable opcion. A esto es lo que me refiero cuando indico que la variable que se evalúa en la condición debe poder cambiarse dentro de la misma estructura, ya que de no hacerlo opcion siempre será igual a s, por lo que el bucle se repetirá eternamente. Al volverse a evaluar la condición ahora se revisará si el usuario tecleo s o n; si es s se repite el mensaje y si es n se continua con las instrucciones debajo de la estructura de repetición, que en este caso es terminar el programa.
  • Otra acotación, si por descuido o intención el usuario ingresa la S (mayúscula), al evaluar la condición ésta no se cumplirá, ya que se esta preguntando si opcion == 's', la cual esta en minúscula, para que se puedan evaluar ambas posibilidades conviene agregar lo siguiente:
    • opcion == 's' || opcion == 'S'; las dos barras es un operador OR Lógico y lo que esta indicando que evalúe si opcion es s o S. Si cumple alguna de las dos se ingresará al bloque de código del WHILE.
  • Veamos también la variable opcion se inicializó con s, esto fue así para garantizar que ingresará al código al menos una vez. Supongamos por un momento que no se hubiese inicializado la variable; al llegar a la condición y preguntar si opcion == 's' se obtendrá como resultado que no se cumple la condición por lo tanto no se ejecutará las instrucciones dentro del WHILE. Para confirmar eso, prueben el código quitando la inicialización, ejecútenlo y vean ocurre.


DO WHILE

La estructura WHILE obliga a evaluar la condición al inicio, por lo que si queremos que el código se ejecute al menos una vez debemos garantizar que la condición se cumpla, tal como lo vimos en el apartado anterior. En C++ se cuenta con una estructura de repetición similar al WHILE original, pero que evalúa la condición al final de la estructura, lo que permite que las instrucciones que se encuentran dentro de él se ejecuten una primera vez. La sintaxis del DO WHILE es la siguiente:

do {
   instrucciones;
   ...
}while (condición);

Aquí podemos notar algunos cambios algo evidentes:
  • El primer cambio que se observa, en sintonía con su propuesta, es que la condición se esta evaluando al final de la estructura, por lo que el bloque de instrucciones dentro del DO WHILE se ejecutarán al menos una vez.
  • La instrucción while (condicion); debe terminar con un punto y coma (;) de lo contrario habrá un error en la sintaxis.
Hagamos un ejercicio para ver el funcionamiento de esta estructura:

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

int main (){
   int i = 0, num;

   cout << "Indique de cuál número desea saber su tabla de multiplicación" << endl;
   cin >> num;
   do {
      cout << i << "x" << num << " = " << i*num << endl;
      i = i+1;
   } while (i < 11);
   return 0;

} 

El ejercicio anterior se diseño para que muestre la tabla de multiplicar de un número que el usuario ingrese. Hagamos su análisis:
  • Se crean dos variables i que funcionará como multiplicador el cual se incrementará después de cada operación y num que es el número al que se le hará la multiplicación y será indicado por el usuario. 
  • Luego se le indica al usuario que ingrese el número al cual se le generará la tabla de multiplicar. El dato se guardará en la variable num.
  • Se entra en la estructura DO WHILE y se muestra la multiplicación de la variable i*num. Esta línea del cout posee mucha concatenanción, esto se hizo para darle un mejor formato de vista.
  • Luego se incrementa el valor de la variable i en uno (es una forma de hacerlo, más adelante mostraré otra forma de incrementar una variable).
  • Y por último se evalúa la condición, la cual verá si i es menos que 11, de ser así se vuelve a repetir el código, de lo contrario se termina la estructura de repetición.

Fíjense que su objetivo son similares lo que cambia es el momento en la que se evalúa la condición, lo cual trae como consecuencia que en éste último se garantice una pasada por las instrucciones. 


Bien, esto es todo con respecto a estas dos estructura, pero antes de cerrar este artículo quiero mostrarle un ejercicio con la que se unirá ambas estructura para que noten el potencial que se puede lograr con ellas: Este ejercicio combinará ambos ejercicios anteriores, es decir generará la tabla de multiplicación de un número ingresado por el usuario, una vez mostrada se le preguntará si quiere repetir la operación.
#include <stdlib.h>
#include <iostream>
using namespace std;

int main (){
   char opcion = 's';
   int i = 0, num;

   while (opcion == 's' || opcion =='S'){
     cout << "Indique de cuál número desea saber su tabla de multiplicación" << endl;
     cin >> num;
     do {
        cout << i << "x" << num << " = " << i*num << endl;
        i = i+1;
     } while (i < 11);
     cout << "Desea repetir la operación? s/n ";
     cin >> opcion;
     i = 0;
   }
   return 0;
} 

Espero les haya gustado este capítulo, si tienen alguna duda comentén, con gusto les responderá. Hasta pronto.