Mostrando las entradas con la etiqueta C. Mostrar todas las entradas
Mostrando las entradas con la etiqueta C. Mostrar todas las entradas

07 noviembre 2023

Archivos de texto en C++

Los archivos de texto son muy usados en estos tiempos, a pesar de que hay sistemas DBMS y mil otras cosas para almacenar datos. Pero, a pesar de todo, siguen siendo muy útiles. Y programar archivos de texto en C++ es muy sencillo y eso te voy a mostrar en este post de mi blog donde quiero mostrarte como abrir, escribir a, leer de y cerrar archivos en este lenguaje.

Abrir un archivo
Antes de poder hacer cualquier otra cosa, hay que abrir el archivo. Para hacerlo, utilizamos la clase fstream. Puedes abrir un archivo para escritura (ofstream), lectura (ifstream), o ambas (fstream). Aquí tienes un ejemplo de cómo abrir un archivo para escritura:

Escritura en un archivo de texto
Ya que esté abierto el archivo, se escribe texto en él usando el operador << como se ve en este ejemplo:


Leer del archivo
Para leer desde un archivo, primero abrimos el archivo en modo lectura (ifstream) y luego usamos el operador >> o la función getline para leer los datos. Aquí hay un ejemplo de lectura utilizando getline:


Cerrar el archivo
Es importante recordar cerrar el archivo una vez que hayamos terminado de trabajar con él. Esto garantiza que todos los datos se escriban o lean correctamente y que el archivo quede en un estado consistente.

Ejemplo completo
Aquí está un ejemplo que muestra cómo abrir un archivo, escribir en él y luego leer su contenido:


Espero que todo esto te sea útil. ¡Hasta la próxima!


09 octubre 2023

Revolviendo vectores

El otro día estaba tratando de hacer un programa que te permite jugar 21 (Blackjack), aunque no fuera de forma "completa", más bien como un ejercicio del uso de vectores, clases y objetos en C++.

Uno de los problemas era revolver una baraja. Como ya he hablado de cómo generar números aleatorios en C++, pensé una forma de generar números al azar e irlos cambiando con los que están, pero supuse que debía haber una mejor manera, algo más sencillo. Y sabía que en Java había forma de trabajar con ArrayLists, así que me puse a buscar cómo se puede hacer en C++. Y sí, hay una función que me puede "mezclar" al azar (o pseudoazar, como hemos dicho cuando hablé de números aleatorios), e incluso escribí brevemente sobre cómo usarlo, pero aquí te explico cómo usarlo con un ejemplo más completo.

El método en cuestión es el random_shuffle y para poderlo usar, tienes que incluir la librería <algorithm>. Y necesita el uso de números aleatorios (si no sabes cómo funcionan, lee primero este post de mi blog), así que incluimos la librería <cstdlib> para usar el srand y el rand, y la librería <ctime> para obtener la hora del sistema para inicializar el generador de números pseudoaleatorios. Así que la pura parte de la declaración, quedaría así:

Después de esto, en el main() antes en hacer cualquier otra cosa, inicializo el generador de números aleatorios con el reloj del sistema, como se ve aquí:

Aquí viene una parte que va en el main() y también estoy mostrando el método de la clase Baraja (al rato pongo todo el código) que barajea (revuelve) los elementos del vector. También agrego una función, llamado myrandom, que no pertenece a ninguna clase, que genera los números aleatorios para ser usados en el método random_shuffle.

Aquí pongo el código completo. El archivo tiene una clase Carta que guarda el palo (corazones, trébol, etc.), el valor o rango (As, 2, 3, etc) y si está visible o no. Tiene un constructor que recibe dos cadenas (palo y rango) y métodos para voltear una carta, devolver los datos de una carta, saber si está visible o no y para devolver el valor (rango).

Luego tiene una clase Baraja que tiene un vector de objetos Carta, un constructor que inicializa el vector con todas las cartas de una baraja inglesa (para eso hay un par de arreglos para ayudar a inicializar) y métodos que te devuelve una carta (y lo quita del vector), para barajear la carta (lo que mostré en el código anterior), para imprimir todas las cartas en la baraja, y una que nos indica si la baraja está vacía o no.

También hay una clase Jugador que tiene las manos de cada jugador: guarda un vector de tipo Carta y una cadena con el nombre del jugador. No tiene constructor y tiene métodos para Agregar una carta al vector del jugador, devolver el número de cartas que tiene, mostrar sus cartas, asignar un nombre al jugador y para mostrar el nombre del jugador.

El main() declara una Baraja y un vector de Jugador, inicializa la baraja (con un código que mostré con anterioridad), pide número de jugadores, pide las cartas para cada jugador, revisa si alguien ganó justo después de repartir las cartas (poco probable, pero hay que revisar) e inicia un ciclo donde muestra cartas del jugador y le pregunta si desea otra carta, revisa si perdió o si ganó. Si nadie ha ganado, al final determina el ganador (el que más cera esté de sumar 21).

Con esa explicación, ahora sí les dejo el código completo del programa, esperando que les sea de utilidad. ¡Saludos!


11 septiembre 2023

Ordenando vectores

Hace unos días les mostré como "revolver" el contenido en un vector en C++ (después de haber explicado, hace mucho, como usar vectores y luego iteradores), pero para otro problema tenía que ordenar un vector.

Encontré esta página que lo explica muy bien, y luego lo apliqué a un ejemplo práctico que ordena un vector que tiene datos de diferente tipo. De esta manera pueden ver cómo funciona el método sort con mis propias funciones de comparación.

Te aconsejo de leer el post que explica el sort y aquí te dejo mi código de ejemplo:



Espero que te sea útil. ¡Hasta la próxima!

05 septiembre 2023

Revolviendo un vector en C++

Hace tiempo escribí sobre cómo usar vectores en C++ y como usar iteradores. Sin embargo, el otro día quise "revolver" (hacer un shuffle) a un vector y se me complicó un poquito encontrar la solución.

En Java existe Collections.shuffle y quería algo similar para un vector. Y, después de experimentar un rato y leer varias páginas, encontré que existe una función shuffle que recibe dos iteradores (del inicio y final del vector) y un motor de generador de números aleatorios. Aquí les dejo el ejemplo simple que hice:


Espero que esto les sirva en sus proyectos. ¡Saludos!

21 enero 2023

Condiciones en C: el Gato 2.0

Hace años publiqué una explicación sobre condiciones en C. En aquel tiempo lo hice todo en audio porque tenía un alumno con debilidad visual (casi ciego). Pero el servidor donde subí el audio cerró sin decir agua va y el artículo no sirve de mucho: puedes ver parte de la explicación y el código, pero se pierde mucho. Asi que, como sigo hablando de C con mis alumnos (y seguiré haciéndolo), es hora de volver a explicarlo.

Uno de los pilares de la programación es hacer que el programa tome decisiones.  En otro ámbito, eso es lo que los papás esperamos de nuestros hijos: de que algún día puedan tomar las decisiones correctas.  Pero al tratar con la computadora es más fácil: nunca va a renegar ni a ignorar lo que le diga (como lo suelen hacer mis hijas) porque siempre va a hacer lo que le indique y siempre lo hará igual.  Claro que mis niñas pueden hacer millones de cosas que mi computadora no, y las amo millones de veces más que mi compu, pero esa es harina de otro costal.

Volviendo al mundo de la programación, casi todos los lenguajes tienen la instrucción IF que nos permite definir las instrucciones que se van a ejecutar en caso que se cumpla alguna condición.  C no es la excepción y en el siguiente video te voy a explicar un programa que usa condiciones IF en C (o C++ que para este caso, da lo mismo).

En el video explico un programa ya hecho, un ejemplo que usa IFs. Si no tienes idea de qué es el IF, te sugiero revisar esta página de Nacho Cabanes que lo explica. Luego regresas a este video.

Aquí está el video:

Y aquí está el código del programa:

26 octubre 2021

Memoria dinámica en C++

Hace mucho tiempo, escribí acerca de apuntadores en C. Sin embargo, ya tocaba modernizarlo para que funcionara con C++.

En este video te explico como está la onda y abajo pongo imágenes con el código que aparece en el video para que lo puedas ver con más calma.

Éste es el primer ejemplo:

Y éste es el segundo:

Espero que te haya sido útil. ¡Hasta la próxima!

08 mayo 2021

Operadores en C/C++/C# y otros

Ayer en clase de introducción a la programación, mientras explicaba operadores aritméticos, se me ocurrió este meme. Ya sé que es malo, pero aquí esta.



19 marzo 2020

Cadenas en C

Antes había hablado sobre la manera de trabajar con cadenas en C y C++. Pero en aquel entonces, por hacer una mezcla y cubrir el mismo tema en ambos lenguajes (que son muy similares), no quiero que se hagan bolas, así que aquí les muestro como hacerlo en C.  Aquí está el video que explica todo:


Y aquí puedes descargar el código fuente del video.

Si tienes dudas con arreglos, sería bueno que leyeras este post. ¡Saludos!

22 septiembre 2017

Características del lenguaje C

Es muy común que mis alumnos me preguntan si el lenguaje C todavía sirve o es usado para algo.  No solo porque es el "padre" de casi todos los lenguajes de programación modernos, sigue siendo de muchísima utilidad.

Hoy encontré éste artículo de makeuseof que detalla las características del lenguaje C, incluyendo una breve historia (más breve que la que escribí aquí) y algunas de las características que la hacen un lenguaje sumamente útil.

Aquí les dejo la liga: http://www.makeuseof.com/tag/characteristics-of-c-programming/

P.D. La imagen del lenguaje C usando en este post es cortesía de la página C Online Test.

21 junio 2016

Curso de programación en C

En este blog hay muchas entradas sobre lenguaje C, pero están de forma dispersa, sin orden.  Mi blog fue pensado en aportar material para mis alumnos, pero son apoyo de cursos que doy de forma presencial y solo les aporto las ligas hacia el material adicional en este blog.

A petición de un muy querido exalumno, pongo este temario de C con recursos (tanto propios como de terceros) que explican cada tema.  Espero que esto sirva como un curso en línea o para despejar dudas en diversos temas.

Intoducción al curso
Espero que con estas referencias puedan aprender un poco más de C.  Me parece un curso completo, que comienza por lo básico y va avanzando hasta llegar a tener un buen dominio del lenguaje.  Si tienes ligas o material de otros sitios que crees que sería útil incluirlo aquí, déjeme un mensaje.

Nota: la imagen de C que usé en este post lo tomé del blog de Aficiones LAMS

05 noviembre 2011

Sobrecargando operadores en C++

Una de las ventajas de la programación orientada a objetos es el polimorfismo (como expliqué aquí hace años).  Esto quiere decir que puedes tener varias funciones con el mismo nombre, pero con diferentes parámetros y/o tipos que devuelve.  Entonces en tu programa, al llamar a la función, va a elegir la versión de la función que necesitas.  Eso te ahorra mucho código.
Algo similar se pueden hacer con los operadores (que técnicamente no es polimorfismo como tal).  Por ejemplo, el operador + está definido en C y C++ para sumar dos números (sea int, double, float u otro tipo de dato numérico).  Pero C++ agrega una clase string que sobrecarga el operador + para que, cuando trabaje con objeto de tipo string, los concatene (o sea que “Tony ” + “Valderrama” = “Tony Valderrama”).  De la misma forma lo puedo hacer con diferentes clases para que estas operaciones funcionen de forma lógica con otros tipos de datos.
Para ejemplificar esto, hice una clase que almacena Fechas (llamada sabiamente Fecha, jeje) que permite sumar dos fechas (operador +), agregar un día (operador ++ postfijo) y por medio de una función amiga modifico el comportamiento del operador de flujo de salida (<<, tipo que se usa para la instrucción cout).  Como te imaginarás, sumar fechas es diferente a sumar dos números porque hay que validar que los números de meses sean correctos (hay meses de 31, 30, 28 o 29), que los meses no pasen de 12, etc.  Aquí está la definición de la clase:
00-Class Fecha
Aquí declaro dos constructores: uno de ellos, el default, que no lleva parámetros y otro al que se le pueden pasar 3 enteros (para asignarle día, mes y año, respectivamente) para inicializarlo desde que crea el objeto.  Luego viene la función que sobrecarga el operador +, luego el que sobrecarga la operación ++ postfijo.  ¿Pero qué es postfijo?  Es cuando el operador ++ lo escribes después del nombre de la variable/objeto.  Como saben, no es exactamente los mismo ++x (prefijo) que x++ (postfijo) porque en el primero se encarga de incrementar la variable antes de realizar la operación, mientras que el segundo primero hace la operación y luego incrementa.  Ejemplo:
int x=3;
cout << ++x;     // Esto imprime 4 porque incrementa antes del cout
cout << x++;     // Imprime 4 y luego incrementa el valor de x
Una vez declarada la versión postfijo, sobrecargo el mismo operador ++ pero ahora el prefijo.  Si te fijas hay solo 2 diferencias entre la declaración de uno y otro: el postfijo devuelve un objeto Fecha y recibe de parámetro un int; mientras el otro devuelve un apuntador al objeto Fecha y no recibe parámetros. Más adelante en el código se verá la diferencia más clara (espero). Después de esto, declaro una función amiga (friend) que se va a encargar de sobrecargar el operador <<, otra que sobrecarga el operador >> y otras dos funciones: una que devuelve el día máximo de acuerdo al mes y año (necesitamos el año por aquello de los años bisiestos) y otra función que le asigna valores al día, mes y año (este lo usé para probar la asignación antes de sobrecargar el operador >>).
Cabe recalcar que los operadores >> y << no son funciones miembro de la clase y devuelven streams (istream es Input stream o flujo de entrada y ostream es Output stream o flujo de salida).  Son muy similares en su función a los strings pero mucho más versátiles para algunos usos.
Volviendo al código puedes observar que los constructores no tienen ningún chiste.  Aquí está el código:
01-Constructores
El primer operador que sobrecargo es el +.  Aquí está el código:
02-Operador
Supongamos que en mi función main() declaro dos objetos te tipo fecha llamados A, B y C.  Si escribo la operación C=A+B se ejecuta esta función.  El valor de A sería el “local”, es decir, cuando hago referencia a dia, mes y anio, estoy accesando estas variables del objeto A.  El parámetro que le paso (llamado x) tendría los valores almacenados en B. así que, cuando hago referencia a x.dia, x.mes y x.anio me estoy refiriendo a estos campos en el objeto B.
Una vez aclarado de los parámetros, pasemos a ver qué hace esta función.  En x recibe el valor de la fecha que se le va a sumar (días, meses y años).  Luego declaro un objeto Fecha (Temp) donde voy a realizar la suma de las fechas.  La variable mex es temporal para el cálculo de meses.  Sumo los días y años en Temp y luego calculo los meses: si el día es mayor al número máximo de días que corresponde al mes, le resto el número de días máximo aceptados, incremento el número de meses y si corresponda cambiar de año, incrementa el año y le resta 12 meses.  Al final del ciclo le sumo los meses y vuelvo a verificar si toca cambio de año.  Al final devuelvo la variable Temp que contiene la suma.
Ahora pasamos al operador ++ postfijo.  Aquí está el código:
03-Operador
Si se fijan, el parámetro de tipo int no me sirve para nada, bueno, sirve para distinguir el ++ postfijo del prefijo como ya argumenté con anterioridad.  Ni siquiera le pongo nombre de variable porque no lo voy a usar.  Creo una Fecha, llamado Temp, para hacer el cálculo de la fecha más un día.  El apuntador this es una forma elegante de referirse a esta misma clase y lo uso para inicializar a Temp.  Altero los valores de ésta clase (apuntador this) para incrementar un día y luego revisar si toca cambio de mes, y si toca, incremento el año.  Al final devuelvo Temp y ya terminé.
El código del operador ++ prefijo es idéntico.  Lo único que cambia es que devuelve la referencia (apuntador) a un objeto Fecha y no recibe un entero como parámetro.  Aquí está:
04-Operador
Ahora una función muy simple para devolver el número máximo de días que tiene cada mes.  Lo uso mucho para validaciones.  La idea es que enero, marzo, mayo, julio, agosto, octubre y diciembre tienen 31 días, mientras que abril, junio, septiembre y noviembre tienen 30.  Como saben el mes especial es febrero que puede tener 29 o 29 días dependiendo si el año es bisiesto o no.  Aquí está el código:
05-MaxDiaMes
Otra función que no tiene chiste es el que te permite asignar fecha.  Aquí está:
06-AsignaFecha
Ahora voy a hablar de la función amiga que sirve para sobrecargar el operador <<.  Aquí está el código:
07-Operador ostream
El objeto de tipo ostream es igual al que recibe la función cout.  Así que básicamente debo hacer lo mismo que haría en un cout para dejar el texto como quiero, pero en lugar de enviarlo al cout, lo mando a x.  Esta función devuelve una referencia al objeto ostream que hice.
Para terminar con las funciones, falta el que sobrecarga al operador .  Hagan de cuenta que ahora estoy trabajando con el cin (el objeto de tipo istream es el mismo tipo de objeto que cin).  La función ignore de la clase istream se encarga de ignorar el siguiente caracter del istream (en mi caso es una diagonal entre día y mes, y otra diagonal entre mes y año).  Aquí está el código:
08-Operador istream
Para terminar, hice la función main() que prueba todo lo anterior.  Aquí está:
09-main
Este ejemplo fue hecho con Visual C++ 2010 y puedes descargar el proyecto en formato RAR aquí.  ¡Saludos!

05 septiembre 2011

Vectores, iteradores y STL…

C  Hace unos días escribí acerca del uso del contenedor vector para poder manejar una lista en C++.  Aunque el vector es muy útil, resulta más interesante y de mayor utilidad si agregamos el uso de iteradores (iterators).  Los iteradores (no sé si esa sea la traducción correcta, pero es lo que más “le suena”) son importantes porque nos permiten accesar de forma fácil a los elementos del contenedor (en este caso un vector), algo así como los apuntadores, solo que más sencillos de manejar y menos susceptibles a errores por parte del programador.  También es importante saber usarlos porque son necesarios para poder usar una serie de algoritmos que ya están programados en la STL y que toman iteradores como parámetros.
Pero, como dijo Jack el destripador: “vámonos por partes”.  Primero voy a decir cómo declarar un iterador, luego como recorrer el vector usándolo, luego funciones que pueden ser de utilidad.

Declarando un iterador

No hay que incluir ninguna librería nueva porque el vector ya lo contiene.  Para declarar uno, la sintaxis sería la siguiente:
vector<tipo>::iterator nombre;
por ejemplo
vector<string>::iterator miIterador;
declara un iterador a un vector de elementos que son de tipo string llamado miIterador.  También es posible declarar iteradores constantes, es decir, que no pueden cambiar de valor.  Esto se haría así:
vector<string>::const_iterator it;
Este ejemplo declara un iterador constante llamado it.

Recorriendo el vector

Si recuerdas en el programa anterior (y si no recuerdas, lee el post anterior que es vital para entender este rollo) podía recorrer el vector manejándolo como si fuera un arreglo.  El código que cumplía tal fin era este (suponiendo que inventario es un vector):
for (unsigned int i = 0; i < inventario.size(); ++i) {
    cout << inventario[i] << endl;
}

Esto está bien, pero con un iterador, puedo hacerlo de la siguiente manera (suponiendo que it es un iterador):
for (it = inventario.begin(); it != inventario.end(); ++it) {
    cout << *it << endl;
}

Fíjate en el operador * que funciona igual que con los apuntadores.

Modificando elementos del vector

También es posible cambiar un elemento del vector.  Supongamos que quiero cambiar lo que tengo de inventario en el primer elemento.  Esto lo podría hacer posicionando el iterador en el primer elemento y usar la notación * para cambiar el valor.  Claro que si quieres cambiar otro elemento, lo tienes que encontrar primero.
it = inventario.begin();
*it = “Guadaña”;

Funciones y elementos del vector

Es posible hacerlo con la misma notación que los apuntadores.  Es decir, si quiero saber el número de elementos del vector inventario puedo hacerlo de cualquiera de estas maneras: (*it).size() o it->size()

Insertando elementos en cualquier posición

Para esto me sirve la función insert.  Supongamos que quiero poner antes del primer elemento un objeto que acabo de encontrar.  Entonces haría lo siguiente:
inventario.insert(inventario.begin(), "Botella llena de Curare");
Claro que si lo quieres insertar en otro lugar, solo posiciona un iterador en el lugar en donde quieres insertarlo y escribe eso en lugar del inventario.begin().

Eliminando cualquier elemento

El contenedor vector tiene la función pop_back para eliminar el último elemento, pero nada más.  ¿Y si quiero eliminar lo que tengo en la posición 3 de mi inventario?  Con iteradores puedo hacerlo de la siguiente manera:
inventario.erase((inventario.begin() + 2));

Y para terminar, referencias y atracciones futuras

Con esto más o menos cubrí lo que puedes hacer con un iterador con el contenedor vector.  Faltaría ver como usar algoritmos de la STL para hacernos la vida todavía más sencilla porque puedo aprovechar funciones ya creadas para ordenar, mezclar, etc.
Para crear este artículo, me basé en el mismo libro de la vez anterior (vuelvo a poner la referencia abajo) y la imagen del C++ chuvidubis es cortesía del blog de Etienne GT.  ¡Hasta la próxima!

31 agosto 2011

Librería estándar (STL) y Vector

vectorx¿Para qué reinventar la rueda si a alguien ya se le ocurrió?  Es mejor usarla, ¿o no?  Este mismo argumento aplica para los programadores: ¿para qué programar cosas que ya están hechas de forma muy eficiente por otros programadores?  Sería una pérdida de tiempo.
Por eso los programadores podemos aprovechar la Standard Template Library, conocido como STL, o librería estándar de plantillas (se oye pésimo en español, pero esa es la traducción).  El STL contiene varios contenedores (valga la redundancia) que te permiten hacer diferentes cosas.  Hay contenedores para arreglos dinámicos (vector), colas (queue), heap (heap), listas ligadas (list), árboles (set), pilas (stack), etc.  Claro que cada uno de estos contiene un chorro de algoritmos que facilitan la vida para usarlos, manipularlos, llenarlos y casi cualquier otra cosa que termine con “arlos”, jeje.
En este artículo voy a hablar un poco sobre el uso del contenedor vector (tocayo del memorable malvado de la película “Mi villano favorito”, que aparece en la imagen de este mensaje).  Básicamente es un arreglo dinámico, es decir, un arreglo que puede cambiar de tamaño (uno de los inconvenientes de los arreglos comunes y corrientes).
Usar el contenedor vector tiene muchas ventajas sobre los arreglos, por ejemplo:
  • Los vectores pueden crecer y los arreglos no.
  • Los vectores pueden usar algoritmos programados en el STL.  Los arreglos no.
Aunque no todo es pura dulzura porque hay también algunas desventajas en el uso de vectores:
  • Necesitan más memoria.
  • Cuando los vectores crecen mucho, disminuya el rendimiento.
  • Los vectores no están disponibles en todas las plataformas.

Antes de empezar…

Antes que nada, hay que incluir la librería que contiene al contenedor (valga la redundancia) vector.  Como está en el namespace std, no hace falta incluir el “.h”.
#include <vector>

Declarar un vector

Para declarar un vector, la sintaxis básica es la siguiente:
vector<tipo_de_dato> nombre_de_variable;
Por ejemplo, si quiero declarar un vector de tipo string con nombre inventario, escribo:
vector<string> inventario;
También puedo decirle que comience con 10 elementos en el vector así:
vector<string> inventario(10);
Y si quiero inicializar los 10 elementos con la cadena “Nada”, escribiría:
vector<string> inventario(10,”Nada”);
Finalmente, si quiero inicializar mi vector con el contenido de un arreglo (en este ejemplo, se llama Arr), escribiría:
vector<string> inventario(Arr);

Funciones

Las funciones más usuales de este contenedor son las siguientes:
  • push_back – agrega un elemento al final del vector.
  • size – devuelve el número de elementos que están almacenados en el vector.
  • pop_back – elimina el último elemento del vector.  esto hace que el vector cambie de tamaño (se hace más pequeño).
  • clear – elimina todos los elementos del vector.
  • empty – Devuelve un valor de verdadero si el vector carece de elementos.

Ejemplo que usa el contenedor Vector

Aquí les presento código hecho en Visual C++ 2010.  Es un ejemplo simple que pone en práctica todo lo que escribí con anterioridad.  Si necesitas, aquí puedes descargar un archivo comprimido con todos los archivos y carpetas que conforman la solución.
CódigoVector

Atracciones futuras

Para que puedas trabajar mejor con vectores y que se puedan aprovechar mejor, es importante hablar de iterators (¿iteradores?).  De éstos hablaré en mi próximo post de C++.

Referencias para mayor información

Si quieres saber más, te recomiendo el sitio de GabaWiki sobre STL Vector.  Abajo pongo la referencia a un libro que me ayudó a crear este post.
La imagen de Vector que aparece al principio es cortesía de USA Today.

24 agosto 2011

Números aleatorios en C++

numeros-300x290Los números aleatorios son de gran importancia en la programación.  Necesitas números aleatorios para hacer girar unos dados, repartir cartas, posicionar enemigos en la pantalla y tal vez hasta controlar sus movimientos.  Sin embargo, crear un algoritmo para generar números aleatorios es todo un problema porque, ¿quién sabe cómo le hace para pensar en un número al azar?  ¿Acaso sigue algunos pasos?  No, es un proceso mental fácil de realizar, pero que no lleva algún procedimiento preciso y esto hace que sea casi imposible hacerlo en la computadora.  Lo más cercano es una función matemática que calcula números al azar.  Aquí voy a presentar un ejemplo que puede ser útil para generar números aleatorios usando funciones de C++.

Antes de empezar de lleno, honor a quien honor merece.  Este ejemplo y parte de la explicación es una traducción/adaptación de un artículo que leí en la página de daniweb.com, escrito por un tal Bob (¿acaso será el constructor?  Bueno, mal chiste…).  Voy a ir explicando funciones, constantes y una que otra monería y al final viene un programa que ejemplifica su uso.

La funciones que se necesitan para generar números al azar se encuentran en la librería cstdlib, así que hay que incluirlo al principio del código.  Para generar el número aleatorio se hace uso de la función rand, misma que genera un número al azar entre 0 y la constante RAND_MAX (que vale generalmente 32,767 pero puede variar según el compilador de C++ que estés usando).

rand funciona de maravilla, pero el problema es que cada vez que ejecutes el programa, te va a mostrar exactamente la misma secuencia de números, lo cual puede resultar interesante la primera vez, pero pierde chiste en las ejecuciones subsecuentes del programa.  Para remediar esto, se puede inicializar el generador de números aleatorios con la función srand, misma que recibe un número entero que inicia el pseudogenerador de números aleatorios.  Pero si cada vez lo inicializas con el mismo entero, el problema no se quita: sigues generando la misma serie de números.  Lo mejor es usar el reloj del sistema para que te dé la hora actual, lo conviertes a entero y así cada vez es diferente.  Para eso usamos la función time que está en la librería ctime.

Una vez resuelto el problema de generar una secuencia de números aleatorios únicos, ahora hay que abordar la manera de generar números en un cierto rango.  La fórmula para hacerlo sería la siguiente:

numero_aleatorio=rango_inferior+int(rango*rand()/(RAND_MAX+1.0))

donde rango_inferior es el valor mínimo del rango del que se desea un número y rango es la diferencia entre el valor mayor y el menor.  Por ejemplo, si quiero generar un número aleatorio entre 6 y 15 y guardarlo en un entero que declaré con el nombre de X, la instrucción sería:

X=6+int((15-6)*rand()/(RAND_MAX+1.0))

Creo que con esta explicación ya puedes generar los números aleatorios que necesites para cualquier aplicación.  Aquí les dejo el código fuente de un programa que ejemplifica todo lo anterior.  ¡Hasta la próxima!

(Imagen de los números aleatorios cortesía del blog “La aventura de las matemáticas”)

aleatorio.cpp

31 mayo 2011

Listas Simplemente Ligadas

Las listas simplemente ligadas son una estructura de datos que nos permite almacenar cualquier cantidad de datos.  La ventaja principal es que la memoria que ocupa es solamente la necesaria a diferencia de un arreglo que puede desperdiciar memoria que no está en uso (como la memoria que se desperdicia con los arreglos: generalmente declaras un arreglo del tamaño máximo que se podría llegar a usar aunque muchas veces no se llena).
La lista simplemente ligada puede ser usada para crear estructuras de datos más complejos.  Consiste en una secuencia de nodos, donde cada nodo contiene información y un apuntador al nodo que sigue.  El último nodo apunta a un valor nulo (NULL) lo cual indica que es el final de la lista.  De esta manera se puede recorrer la lista de forma secuencial siempre y cuando tengas un apuntador al primer elemento de la lista, como se ve la siguiente imagen (tomada del blog de xrom):
lista
O, si quieres, puedes conceptualizarlo como un tren de vagones donde el tienes un apuntador a la locomotora y el apuntador al siguiente que hay en cada nodo es el mecanismo con el que se engancha al vagón que le sigue (Imagen cortesía de Medieducativos):
t106-tren-cuatro-vagones
En este post me voy a enfocar en 3 cosas: agregar un nuevo nodo a la lista (al final), mostrar todos los nodos (recorrerlo) y eliminar un nodo de la lista.  Antes de entrar a estos temas, hay que ver como se declara la estructura y la forma en que se usa.  En este ejemplo, estoy haciendo una lista que guarda algunos datos de videojuegos.  Aquí está la declaración de la estructura y una variable global que apunta al primer nodo de la lista:
LSL-01
La función principal del programa (main) no tiene mayor ciencia.  Básicamente solo muestra un menú y pide una opción.  En base a la opción deseada manda llamar funciones para dar de alta, consultar todos o eliminar alguno.   Una nota antes de seguir: este programa lo hice usando el compilador Dev-C++ varsión 4.9.9.2.  Este es el código (haz clic sobre la imagen para verlo más grande):
LSL-02
Voy a comenzar hablando de la función que da de alta un nodo al final de la lista.  Primero declaro dos apuntadores a struct nodo: Temp y Temp2.  Uso la función malloc para apartar RAM para almacenar la estructura de forma dinámica.  Si necesitas saber más sobre el malloc, al final de este post hay ligas a páginas que son referencias para que despejes dudas sobre su sintaxis y función.  Pero a grandes rasgos, el malloc sirve para asignarle memoria y devolver un apuntador hacia el espacio reservado.  Una vez apartada la memoria, lleno el nuevo nodo con los datos (que son los parámetros nom, plat y pre) y al campo Sig le asigno el valor de NULL porque va a ser el último de la lista.  Al final debo reorganizar los apuntadores: el apuntador Sig del elemento anterior debe apuntar a este nuevo nodo.  El código que lo realiza es el siguiente:
LSL-03
Una vez resuelto el problema de dar de alta nodos, voy a explicar la forma en que se puede recorrer la lista y mostrar los elementos.  Esto lo hice en la función llamada MuestraTodo.  Declaro un apuntador llamado Temp (para no perder la costumbre, jeje) con el que voy a recorrer la lista.  Uso la variable i para mostrar el número de nodo en el que voy.  Al principio igualo Temp a Inicio  para comenzar el recorrido.  Y mientras no llego al final (o sea, Temp es diferente de NULL), muestro lo que contiene el nodo.  Fíjate como se accesa un elemento por medio de un apuntador ya que difiere de la forma en que se hace cuando no usas apuntador.  En lugar del punto (.), se usa una especie de flecha compuesta por el guión y el signo mayor que (->).  Aquí está el código:
LSL-04
Ahora lo  único que falta es mostrar la función que elimina un nodo de la lista.  Esto es relativamente sencillo y creo que el código se explica por si solo (le puse un montón de comentarios).  Básicamente es igual que la función MuestraTodo solo que esta vez, en lugar de mostrar el elemento, veo si es igual al que busco.  En caso que sea igual, veo si es el primero de la lista.  Si es el primero, solo cambio el valor de Inicio para que apunte al siguiente, y en caso contrario, modifico el apuntador Sig del elemento anterior (convenientemente apuntado por Anterior) para que apunte al que sigue.  Una vez arreglado los apuntadores, libero la memoria usando la instrucción free.  Al final viene más ayuda sobre free, pero básicamente libera la memoria que es apuntada por un apuntador (valga la redundancia).  Al final devuelve un 1 si lo encontró y lo pudo borrar y un 0 si no lo encuentra.  Este es el código de la función Eliminalo:
LSL-05
Estas son ligas donde puedes encontrar más información sobre malloc y free (y algunas otras funciones que te pueden resultar muy útiles).  ¡Hasta la próxima!

10 diciembre 2009

El Rap del C++, parte II

En mi mensaje anterior les presenté mi debut como rapero con el rap del C++, sin embargo no contaba con un videoclip.  En lugar de andar calificando (como debería porque tengo un montón de trabajos pendientes esperando ser calificados) me puse a crear un videoclip.
Aquí se los presento.  Espero que les sea útil, de su agrado o por lo menos les haga reír un rato.




08 diciembre 2009

El Rap del C++

Algunos alumnos de la prepa que cursan el bachillerato técnico en programación me dieron la idea de hacer un rap sobre el C++.  Hice la letra y elegí un “loop” rapero gratuito, pero la verdad es que eso de rapear no se me da.  De todos modos lo intenté y este es mi resultado.
Les dejo aquí la letra y abajo viene el MP3 con mi voz.  De antemano me disculpo por mi falta de cualidades musicales (bueno, ¿el rap es música?  A mi se me hizo difícil decir muchas cosas muy rápido sin que se me fuera el aliento).  En fin, aquí esta:
Este es el rap
del C más más,
y de eso que los gringos
llaman una "class".
Una clase me define
como es un objeto:
sus datos y funciones
y es muy completo.
La clase es
como el plano de una casa,
aunque no está en RAM
te muestra lo que pasa.
El objeto convierte a
la clase en realidad
y utiliza toda
su funcionalidad.
Como cualquier otra variable
objeto declaras:
escribe el nombre de la clase
y luego el nombre que le das.
Si a variables y funciones
tu quieres accesar,
con el operador punto
lo puedes ejecutar.
Programación con Objetos
permite abstracción,
que algunos lo conocen como
encapsulación.
Abstracción permite
tu vida facilitar
porque datos y funciones
tu puedes agrupar.
Polimorfismo es
una gran aportación,
porque cambia lo que hace
según la situación.
Aunque tenga el mismo nombre,
y esto es sorprendente,
hace lo que esperas
de una forma diferente.
La herencia te permite
mucho código reusar
porque en lugar de escribirlo
lo puedes heredar.
Haz una clase base
con todo lo que quieras
y a un montón de clases
sus cosas les heredas.
Con todo esto dicho
me voy a retirar
porque ya me dieron ganas
de irme a programar.
Y aquí está el audio:
Prometo en el futuro hacer el videoclip con la letra de la canción.  Ya tengo todo listo, pero esta semana dudo tener tiempo para armarlo.
Aquí están los créditos:
94-Créditos
Espero que les guste, o por lo menos les de risa.  ¡Hasta la próxima!
P.D. Si realmente quieren asustar a sus amigos, pueden bajar el MP3 en esta liga.


22 octubre 2009

Apuntadores en C: ¿pa’ onde apunto?

apuntarLa idea de este mensaje es ayudar a que aprendas a aplicar los apuntadores de manera que sean útiles. Los apuntadores son muy útiles en C, como espero que podrás apreciar en este mensaje.  Está por demás decir que no puedo cubrir TODO lo que hay que ver con apuntadores en este mensaje.  Si luego tengo tiempo, puedo escribir una segunda o tercera parte para explicar como usar apuntadores para pasar parámetros a las funciones por referencia, ver su relación con arreglos, usar apuntadores a funciones y a arreglos de caracteres.
¿Y qué son esas ondas que suenan tan complicadotes?
En primer lugar, un apuntador es una variable que almacena una dirección de memoria. Generalmente, si está bien hecho, esa región de la memoria tiene algún dato interesante.
¿Y cómo nos les declaramos?
Se declaran al igual que cualquier variable, solo que se le antepone una asterisco. Estos son algunos ejemplos:
Codigo1
Consideraciones
Recuerda que & sirve para indicar la dirección de memoria donde se guarda el contenido de una variable. Y el * regresa el valor a la cual apunta un apuntador (valga la redundancia). Ejemplos:
Codigo2
Nota: Aquí estoy usando el printf, pero lo puedes sustituir con el cout, si lo prefieres.
Riesgo: si no lo inicializas, lo pagarás muy caro
Cuando declaras un apuntador ya apunta a algún lado (cuando declaras una variable en C simplemente reserva el lugar en la memoria, pero lo que había allí se queda). Tienes que inicializar el apuntador antes de usarlo. Fíjate en el siguiente código:
Codigo3
En el código de ejemplo 2 se muestra la manera correcta de hacerlo (bueno, de programarlo). Se inicializa yPtr a una dirección de memoria ya “apartada” por y.
Si no inicializas una variable cualquiera, a lo mucho no te da el resultado esperado, pero el resto del programa funciona. Pero con los apuntadores hay que tener mucho cuidado ya que pueden apuntar a cualquier lado en la memoria y, por lo tanto, puede alterar datos usados por diferentes variables o funciones. Es como un carro: te puede llevar a cualquier lugar (lo cual te da libertad y facilidad para viajar), pero si conduces sin fijarte por donde vas, puedes sufrir un accidente. Lo mismo sucede con los apuntadores: si no te fijas o controlas hacia donde apunta, puede ocurrir un error grave.
Haciendo algo útil con apuntadores: hostigar funciones
Podemos usar apuntadores con las funciones para hacer “cositas”. Básicamente hay 2 maneras de pasar valores a una función: por referencia y por valor.
Generalmente solo pasamos funciones por valor porque es la manera estándar de hacerlo.  En este caso, si modifico el valor del parámetro pasado por valor, solo se modifica dentro de la función y al terminar ésta, la variable es tragada por un inmenso hoyo negro que lo desplazará hacia una galaxia lejana y se pierde para siempre.
Al pasar valores por referencia, es otra onda. Pasas apuntadores como parámetros a una función y por esto, si alteras el valor contenida en una dirección de memoria (la que se le manda a la función), se altera en todos lados.
Esto se parece cuando llevan un pastel a un cumpleaños. Si se reparte un pastel a cada invitado, se asemeja a pasar por valor porque cada persona hace con su pastel lo que quiere (se lo come allí, se lo lleva a su casa, etc.). Sin embargo, si entre todos comen el mismo pastel, es como si pasaras por referencia, porque todos se reparten el mismo pastel: solo les dicen donde está el pastel, les dan el plato y se sirven. El pastel se va acabando entre todos.
Vamos viendo un ejemplo. Este código (el 4) usa una función muy simple, que eleva un número al cuadrado. De esta manera, que es la forma “ordinaria”, se pasan los parámetros por valor:
Codigo4
El siguiente ejemplo (código 5) pasa la dirección de memoria donde está almacenado el valor. Nótese que la función la declaro como tipo void porque ya no necesito que devuelva nada (porque altero lo que está almacenado en la dirección de memoria directamente).
Codigo5
La función cuadrado_ref(int *x) recibe la dirección de una variable entera (o sea, un apuntador entero), lo almacena de forma local como x y no devuelve un valor. Podríamos decir que el valor que se manda a la función es una “copia” de la dirección de memoria.
Hasta donde vamos, la utilidad del uso de apuntadores en funciones nos permite devolver más de un valor a la función que los llamó (porque cada función solo puede regresar un valor ya que solo puede tener una instrucción return). Y, además, nos permite mandar una cadena de texto a una función (sería imposible hacer esto con los parámetros “normales”, ya que el tipo de datos cadena no existe y tengo que mandar un arreglo de char), como vemos en el código 6, escrito a continuación.
Codigo6
Ahora si, ya con algunas bases, vamos haciendo cosas verdaderamente interesantes.
Reservar memoria de manera dinámica
Al leer este título, probablemente te estarás preguntando qué onda con eso, qué significa, que me fumé… Piensa lo que quieras, pero lo importante es que sepas que hay problemas en algunos programas si no sabes de antemano con cuántos datos vas a estar trabajando.
Supongamos que estás haciendo un programa en C que almacena los datos de los libros que tiene una biblioteca. Como un buen programa, lo diseñas pensando que no sabes de antemano el número de libros que tienen almacenados. A lo mejor es la biblioteca de la primaria federal no. 2, o a lo mejor es la biblioteca principal de la UNAM. Recuerda que un buen programa no debe restringirse a problemas del programador – al usuario le vale madres tus broncas, solo quieren un programa que funcione. Además no puedes estar contratado de por vida en un lugar que use tu programa. Tus aplicaciones deben poder adaptarse a diferentes condiciones o restricciones del lugar donde quieres vender y/o implementar tu solución.
Vamos a comenzar apartando memoria para usar cadenas de manera dinámica. Supongamos que no sabemos hasta la hora de ejecución el tamaño de un arreglo. Esto es una bronca porque si declaras un arreglo de caracteres (o sea, una cadena o “string”) de antemano, a lo mejor reservas memoria de más que no vas a usar o necesitas (si eres de Jalisco, tal vez “ocupes”) más caracteres de los que declaraste.
Fíjate en el código 7. Le pide al usuario el tamaño máximo que puede tener una cadena y reserva el espacio en la memoria. Después le pide al usuario que le dé un valor a la cadena que reservó.
Codigo7
El malloc es una función que aparta memoria. Recibe como parámetro el número de bytes que se van a reservar y devuelve una dirección de memoria (que nosotros asignamos a un apuntador). Se usa la función sizeof que devuelve el número de bytes que se requieren para almacenar un valor. En este ejemplo no era del todo necesario (sabemos de antemano que un char ocupa (bien empleado el término) un byte, pero lo puse porque es muy útil cuando se reserva memoria para usar estructuras (como una lista simplemente ligada, como hablaremos más adelante). El free en este caso no se trata de una relación relajada entre novios, sino que se trata de una función que libera la memoria apartada con malloc.
Lo primero que vas a encontrar en los libros de C++ es que es de mala educación usar el malloc y el free, los cuales son parte de la librería ANSI C (el C estándar que es acatado por todos los compiladores de C, menos el DevC). Sostienen que son “vicios” de programación y por eso el C++ propone el uso de otras funciones para reservar memoria de manera dinámica. Estas son el new y el delete. Pero de estos hablaremos después, ya que quiero centrarme solo en el uso de C estándar en este documento. En otro momento hablaremos de C++ y el uso de new y delete (que son muy buenos para trabajar con clases y objetos).
Bibliografía


El Tony y sus ondas...

Related Posts Plugin for WordPress, Blogger...