sábado, 4 de julio de 2015

Juego: Tres en raya



Esta vez repasaremos matrices, funciones, condicionales e iteraciones, todo esto aplicado en el clásico juego del tres en raya, las reglas del juego son muy conocidas, sólo haré unos cuantos razonamientos previos antes de empezar a programar:

Sobre los turnos

Sabemos que son 2 jugadores que juegan por turnos hasta que alguno gana o el tablero se llena sin ganador (situación de empate).
Vemos que debe existir una iteración que finalice cuando el juego termina:

var fin_juego = false;
var turno = 0; //los jugadores son 0 y 1

while(!fin_juego)
{
//se hace la jugada
//si se gana en este turno fin_juego = true;
//si el tablero se llena sin ganador fin_juego = true;
turno = (turno + 1) % 2; //esto es para cambiar el turno de 0 a 1 y de 1 a 0
}



Sabemos que es un tablero de 9 casillas (3 filas y 3 columnas). En principio todas las casillas están vacías y en cada turno se va llenando alguna de las casillas con la jugada hecha en el turno.
Si todas las casillas están llenas y no hay ganador, entonces es un empate.
Para controlar que el juego termine en empate creamos un condicional en cada iteración que controle si se llenó el tablero o no.

var fin_juego = false;
var turno = 0; //los jugadores son 0 y 1
var casillas_vacias = 9;

while(!fin_juego)
{
//se hace la jugada
casillas_vacias = casillas_vacias - 1; //al hacer la jugada disminuye en 1 las casillas vacías

//si se gana en este turno fin_juego = true;

//si el tablero se llena sin ganador fin_juego = true;
if(!fin_juego && casillas_vacias == 0) fin_juego = true;
turno = (turno + 1) % 2; //esto es para cambiar el turno de 0 a 1 y de 1 a 0
}

Sobre la jugada

El jugador debe seleccionar la casilla que quiere marcar, para esto utilizaremos las filas y las columnas de la matriz que representa al tablero.

var boolean fin_juego = false;
var int turno = 0; //los jugadores son 0 y 1
var int casillas_vacias = 9;
var char tablero [3][3];//matriz de 3 x 3 que representa al tablero
var int fila_jugada;
var int columna_jugada;
var char ficha='0'; //será 'X' cuando el turno sea 1

while(!fin_juego)
{
//se hace la jugada
fila_jugada = leer(); //lee de consola la fila (de 0 a 2) que selecciona el jugador en ese turno
columna_jugada = leer(); //lee de consola la columna (de 0 a 2) que selecciona el jugador en ese turno
if(turno == 0) ficha = '0'; //escoge ficha
else ficha = 'X';
tablero[fila_jugada][columna_jugada] = ficha;
casillas_vacias = casillas_vacias - 1; //al hacer la jugada disminuye en 1 las casillas vacías

//si se gana en este turno fin_juego = true;

//si el tablero se llena sin ganador fin_juego = true;
if(!fin_juego && casillas_vacias == 0) fin_juego = true;
turno = (turno + 1) % 2; //esto es para cambiar el turno de 0 a 1 y de 1 a 0
}

Sobre el control de la jugada ganadora

Cuando se coloca la ficha se hace control en vertical, horizontal y diagonales a ver si es una jugada ganadora.
Para el control horizontal, todas las fichas de la fila_jugada deben ser iguales, por tanto haremos una validación iterando las columnas.
Para el control vertical, todas las fichas de la columna_jugada deben ser iguales, por tanto haremos una validación iterando las filas.
Para el control diagonal, no es necesario realizarlo siempre, sólo cuando la jugada esté en alguna diagonal. Y sólo si está en el medio del tablero la comprobación debe ser de la doble diagonal.
Comprobar diagonal 1
fila_jugada: 0 y columna_jugada:0,
fila_jugada: 2 y columna_jugada:2

Comprobar diagonal 2
fila_jugada: 2 y columna_jugada:0,
fila_jugada: 0 y columna_jugada:2

Comprobar ambas diagonales.
fila_jugada: 1 y columna_jugada:1



Como esta lógica es un poco larga, la encapsularemos en una función y la utilizaremos en cada jugada para comprobar si la jugada es ganadora:

Entrada: ¿Qué necesito?
Tablero, fila_jugada, columna_jugada
Salida: ¿Qué quiero?
Booleano que indique si es una jugada ganadora o no
Función: ¿Cómo lo hago?
Comprobando la Horizontal, Vertical y cuando toque las diagonales.

Boolean esGanador(tablero, fila_jugada, columna_jugada)
{
Boolean jugada_ganadora = false;
//validación horizontal 
if(Tablero[fila_jugada][0] == Tablero[fila_jugada][1] &&
Tablero[fila_jugada][0] == Tablero[fila_jugada][2])
{
return true; //jugada ganadora en la horizontal, termina la ejecución
}
//validación vertical 
if(Tablero[0][columna_jugada] == Tablero[1][columna_jugada] &&
Tablero[0][columna_jugada] == Tablero[2][columna_jugada])
{
return true; //jugada ganadora en la vertical, termina la ejecución
}
//verifica la diagonal 1 (sólo si es necesario)
if( (fila_jugada == 0 && columna_jugada == 0) || (fila_jugada == 2 && columna_jugada == 2))
{
if(Tablero[0][0] == Tablero[1][1] &&
Tablero[0][0] == Tablero[2][2])
{
return true; //jugada ganadora en la diagonal 1, termina la ejecución
}
}

//verifica la diagonal 2 (sólo si es necesario)
if( (fila_jugada == 0 && columna_jugada == 2) || (fila_jugada == 2 && columna_jugada == 0))
{
if(Tablero[0][2] == Tablero[1][1] &&
Tablero[0][2] == Tablero[2][0])
{
return true; //jugada ganadora en la diagonal 2, termina la ejecución
}
}

//verifica la doble diagonal(sólo si es necesario)
if( fila_jugada == 1 && columna_jugada == 1)
{
if((Tablero[0][2] == Tablero[1][1] &&
Tablero[0][2] == Tablero[2][0]) ||
(Tablero[0][0] == Tablero[1][1] &&
Tablero[0][0] == Tablero[2][2]))
{
return true; //jugada ganadora en alguna de las diagonales, termina la ejecución
}
}


return jugada_ganadora; //sólo llega a esta línea de código cuando no es jugada ganadora
}



Incorporamos la llamada a la función en el programa principal
var boolean fin_juego = false;
var int turno = 0; //los jugadores son 0 y 1
var int casillas_vacias = 9;
var char tablero [3][3];//matriz de 3 x 3 que representa al tablero
var int fila_jugada;
var int columna_jugada;
var char ficha='0'; //será 'X' cuando el turno sea 1

while(!fin_juego)
{
//se hace la jugada
fila_jugada = leer(); //lee de consola la fila (de 0 a 2) que selecciona el jugador en ese turno
columna_jugada = leer(); //lee de consola la columna (de 0 a 2) que selecciona el jugador en ese turno
if(turno == 0) ficha = '0'; //escoge ficha
else ficha = 'X';
tablero[fila_jugada][columna_jugada] = ficha;
casillas_vacias = casillas_vacias - 1; //al hacer la jugada disminuye en 1 las casillas vacías

//si se gana en este turno fin_juego = true;
fin_juego = esGanador(tablero, fila_jugada, columna_jugada);

//si el tablero se llena sin ganador fin_juego = true;
if(!fin_juego && casillas_vacias == 0) fin_juego = true;
turno = (turno + 1) % 2; //esto es para cambiar el turno de 0 a 1 y de 1 a 0
}


Con esto tendríamos lo básico para el tres en raya, pero ¿qué pasaría si el jugador pusiera la ficha en un lugar ocupado? ¿qué pasaría si inicialmente el tablero está ocupado?¿No vendría bien agregar una impresión del tablero para que jugador sepa en cada jugada cuáles son sus opciones de juego?

¿Cómo modificaríais el programa para agregar estas funcionalidades?

Enlaces




domingo, 8 de marzo de 2015

Instancia de un objeto

A veces nos resulta complicado controlar que todos los atributos de todas las clases queden bien instanciados, para tener todo esto mejor controlado lo mejor es tener un diagrama de clases para saber qué clases deben instanciar a qué otras.

A continuación os mostraré lo que puede ocurrir si no tenemos bien controladas todas las instancias.

Tenemos una clase llamada Persona:

using System.IO;
using System;
public class Persona{   

  string nombre;   
  int edad;   
  char sexo;      
  public Persona()   
  {      nombre = "";      
         edad = 0;      
         sexo = ' ';   
  }   
  public Persona(string pNombre, int pEdad, char pSexo)   
  {       nombre = pNombre;
         edad = pEdad;
         sexo = pSexo;   
  }      
  public void imprimir()   
  {      Console.WriteLine("Persona-Nombre: "+nombre);
         Console.WriteLine("Persona-Edad: "+edad);
         Console.WriteLine("Persona-Sexo: "+ sexo);
  }
}

Y tenemos una clase llamada Cliente que incluye a un objeto de tipo Persona, en este caso es Cliente el que debe instanciar al objeto de tipo Persona

using System.IO;
using System;
public class Cliente
{
    int codigo;
    Persona persona;
    
    public Cliente()
    {
        codigo = 0;
        persona = new Persona();//es en el constructor del cliente que instanciamos al objeto de tipo Persona
    }
    public void imprimir()
    {
        Console.WriteLine("Cliente-Codigo: "+codigo);
        persona.imprimir();
    }
}

Si ejecutamos este código:

using System.IO;
using System;

class Program
{
    static void Main()
    {
        Cliente cliente = new Cliente();
        cliente.imprimir();
    }
}

El resultado de la ejecución es la siguiente:
Cliente-Codigo: 0                                                                                                                                                                  
Persona-Nombre:                                                                                                                                                                    
Persona-Edad: 0                                                                                                                                                                    
Persona-Sexo: 

Ahora os mostraré lo que pasaría en el caso que en el constructor de cliente NO se instancie el objeto persona, comentaremos la linea

public Cliente()
    {
        codigo = 0;
        //persona = new Persona();//es en el constructor del cliente que instanciamos al objeto de tipo Persona
    }

El compilador no nos avisará, porque no es un error sintáctico, pero el error vendrá en la ejecución:
Cliente-Codigo: 0                                                                                                                                                                  
                                                                                                                                                                                   
Unhandled Exception:                                                                                                                                                               
System.NullReferenceException: Object reference not set to an instance of an object                                                                                                
  at Cliente.imprimir () [0x00000] in <filename unknown>:0                                                                                                                         
  at Program.Main () [0x00000] in <filename unknown>:0                                                                                                                             
[ERROR] FATAL UNHANDLED EXCEPTION: System.NullReferenceException: Object reference not set to an instance of an object                                                             
  at Cliente.imprimir () [0x00000] in <filename unknown>:0                                                                                                                         
  at Program.Main () [0x00000] in <filename unknown>:0   

Vemos que Cliente-codigo si que lo ha impreso correctamente, pero a partir de allí la impresión del objeto persona dio error por NULLReference, esto es porque no está instanciado el objeto.

Ahora que hemos visto la importancia de tener controladas las instancias os mostraré como instanciar con valores al objeto persona a partir de la clase Cliente.

Vimos que la clase Persona tiene el constructor:
   public Persona(string pNombre, int pEdad, char pSexo)
   {
       nombre = pNombre;
       edad = pEdad;
       sexo = pSexo;
   }

Agregamos ahora un nuevo constructor a la clase Cliente que utilice este constructor de la clase Persona:
    public Cliente(int pCodigo, string pNombre, int pEdad, char pSexo)
    {
        codigo = pCodigo;
        persona = new Persona(pNombre, pEdad, pSexo);
    }

Y desde el Main le pasamos todos los valores utilizando este nuevo constructor de la clase Cliente:
    static void Main()
    {
        Cliente cliente = new Cliente(1, "Juan", 30, 'H');
        cliente.imprimir();
    }

Y finalmente vemos la ejecución:
Cliente-Codigo: 1                                                                                                                                                                  
Persona-Nombre: Juan                                                                                                                                                               
Persona-Edad: 30                                                                                                                                                                   
Persona-Sexo: H   





domingo, 23 de noviembre de 2014

Push en una Pila

Cuando nos explican el concepto de Pilas lo principal es que: "el primero que entra es el primero que sale":


con esto parece que tenemos toda la teoría aprendida, el problema viene cuando intentamos implementarlo y no sabemos por dónde empezar.

Por lo general cuando se explica este tema se hace en C++ y utilizando estructuras, por tanto antes de continuar esta explicación os recomiendo leer previamente el post relacionado a estructuras

Vemos en la imagen que cada elemento de la pila está formado por un círculo con un valor y una flecha que lo une con el siguiente círculo, con estos dos elementos formaremos un elemento de la Pila, de esta forma:

struct ElemPila{
  int valor; //esto representa el valor que está dentro del círculo
  struct ElemPila * siguienteElem; //esto representa la flecha, observar que es un puntero
};

Hasta aquí tenemos definida la estructura, pero aún no la estamos utilizando, ahora veremos como desde el programa utilizamos esta esta estructura para agregar los elementos: 1, 2 y 3:

int main()
{
  / /Creamos el primer elemento, con valor 1 y la flecha que apunta al siguiente elemento apunta a NULL
   struct ElemPila* elem1 = new ElemPila; //reserva memoria para el contenido de la estructura
   elem1->valor = 1; //inicializa el valor de la estructura
   elem1->siguienteElem = NULL; //inicializa el valor hacia donde apunta el siguiente
   
   struct ElemPila* elem2 = new ElemPila;
   elem2->valor = 2;
   elem2->siguienteElem = NULL;
   
   struct ElemPila* elem3 = new ElemPila;
   elem3->valor = 3;
   elem3->siguienteElem = NULL;
      
   return 0;
}

Al ejecutar este programa lo que logramos es tener 3 elementos independientes, así:


Como paso siguiente modificaremos el programa para introducir el puntero que será cabeza de la pila e introduciremos sólo el primer elemento a la pila:

int main()
{
   struct ElemPila* elem1 = new ElemPila;
   elem1->valor = 1;
   elem1->siguienteElem = NULL;
   
   struct ElemPila* elem2 = new ElemPila;
   elem2->valor = 2;
   elem2->siguienteElem = NULL;
   
   struct ElemPila* elem3 = new ElemPila;
   elem3->valor = 3;
   elem3->siguienteElem = NULL;
   
   struct ElemPila* cabezaPila; //creamos el puntero
   cabezaPila = elem1; //hacemos que apunte al primer elemento
   
   return 0;
}



Para incluir el segundo elemento en la pila, lo que tenemos que hacer es que el segundo elemento apunte al primero (elem1) y la cabeza de pila apunte al segundo:

int main()
{
   struct ElemPila* elem1 = new ElemPila;
   elem1->valor = 1;
   elem1->siguienteElem = NULL;

   struct ElemPila* elem2 = new ElemPila;
   elem2->valor = 2;
   elem2->siguienteElem = NULL;

   struct ElemPila* elem3 = new ElemPila;
   elem3->valor = 3;
   elem3->siguienteElem = NULL;

   struct ElemPila* cabezaPila;
   cabezaPila = elem1;

   elem2-> siguienteElem = cabezaPila;
   cabezaPila = elem2;

   return 0;
}


Finalmente incluimos el tercer elemento, siguiendo los mismo pasos que el anterior, que es hacer que el siguiente de elemento 3 apunte al elemento 2 y la cabeza de la pila apunte a elemento 3:

int main()
{
   struct ElemPila* elem1 = new ElemPila;
   elem1->valor = 1;
   elem1->siguienteElem = NULL;
 
   struct ElemPila* elem2 = new ElemPila;
   elem2->valor = 2;
   elem2->siguienteElem = NULL;
 
   struct ElemPila* elem3 = new ElemPila;
   elem3->valor = 3;
   elem3->siguienteElem = NULL;
 
   struct ElemPila* cabezaPila;
   cabezaPila = elem1;
 
   elem2->siguienteElem = cabezaPila;
   cabezaPila = elem2;
 
   elem3->siguienteElem = cabezaPila;
   cabezaPila = elem3;
 
   return 0;
}



Si observamos atentamente el código y las imágenes veremos que las variables elem1 y elem2 se quedan apuntando a elementos de la pila que en teoría no se deberían acceder directamente (el acceso es sólo con la cabeza de pila), por tanto podríamos deducir que con una sola variable de tipo ElemenPila sería suficiente. De la misma forma podemos observar en el código que el mecanismo para agregar un elemento es el mismo, por lo tanto podríamos utilizar una función para reutilizar el código. todo esto de esta forma:

#include <iostream>

struct ElemPila{
  int valor;
  struct ElemPila * siguienteElem;
};

using namespace std;


//incluimos una función de impresión para comprobar el orden en que se han guardado
void imprimir(struct ElemPila* cabezaPila)
{
    struct ElemPila* auxiliar = cabezaPila;
    while(auxiliar != NULL)
    {
      cout << auxiliar->valor << " - ";
      auxiliar = auxiliar -> siguienteElem;
    }
    cout << "=====================\n";
}

//función que realiza el push de forma genérica para cualquier elemento
struct ElemPila* push(int pValor, struct ElemPila* elem, struct ElemPila* cabezaPila)
{
   elem = new ElemPila;    
   elem->valor = pValor;
   elem->siguienteElem = NULL;
   elem->siguienteElem = cabezaPila;
   cabezaPila = elem;
   return cabezaPila;
}

int main()
{
   struct ElemPila* cabezaPila = NULL;
   struct ElemPila* elem = NULL;

   cabezaPila = push(1, elem, cabezaPila);   
   imprimir(cabezaPila);
       
   cabezaPila = push(2, elem, cabezaPila);   
   imprimir(cabezaPila);
   
   cabezaPila = push(3, elem, cabezaPila);   
   imprimir(cabezaPila);
   
   return 0;
}


Executing the program....
1 - =====================
2 - 1 - =====================
3 - 2 - 1 - =====================

martes, 21 de octubre de 2014

El elemento neutro de la suma y el producto

Hay recuerdos muy lejanos al aprender a sumar o a multiplicar, en el cole (o en la universidad) nos explicaban las propiedades de la suma y del producto: propiedad transitiva, propiedad distributiva, etc. una de las propiedades (generalmente la primera) es la propiedad del elemento neutro que dice:

A todo número que se le sume cero da el mismo número. -> El 0 es el elemento neutro de la suma

A todo número que se le multiplique por uno da el mismo número. -> El 1 es el elemento neutro de la multiplicación.

¿A que viene todo esto del elemento neutro? Esto va relacionado a operaciones que hacemos de forma acumulativa, por ejemplo:

1. Calcule la suma de los N primeros números enteros.
2. Calcula el factorial de un número N.

En ambos casos, una de las soluciones por las que se podría optar es hacer un bucle que acumule la suma o el producto de esta forma:

//ejemplo 1
Para i=1 hasta N hacer
  suma = suma + i;
Fin_Para

//ejemplo 2
Para i=1 hasta N hacer
  factorial = factorial *  i;
Fin_Para

En ambos casos al estar calculando un valor acumulado, reutiliza el valor de la variable en la iteración anterior (suma utiliza a suma para calcularse a si misma, lo mismo con factorial).
¿Pero qué pasa en la primera iteración? En la primera iteración es necesario que suma y factorial simplemente sean igual a "i", por este motivo es necesario (indispensable) inicializar ambas variables con el valor del elemento neutro. Los algoritmos completos quedarían así:

//ejemplo 1
suma = 0: //variable inicializada con el valor del elemento neutro de la operación que haremos
Para i=1 hasta N hacer
  suma = suma + i //la primera iteración: suma = 0 + i;
Fin_Para

//ejemplo 2
factorial = 1 //variable inicializada con el valor del elemento neutro de la operación que haremos
Para i=1 hasta N hacer
  factorial = factorial *  i: //la primera iteración:  factorial = 1 * i
Fin_Para

Para cualquier cálculo en que utilicemos acumuladores es absolutamente necesario inicializarlos correctamente, por lo general con el valor del elemento neutro de la operación de acumulación.

jueves, 2 de octubre de 2014

Árboles binarios, un ejemplo práctico

Muchas veces nos es difícil entender para qué nos enseñan algo sin explicarnos la aplicación. El tiempo que llevo dando clases particulares he podido ver apuntes con mucho contenido y con pocos ejemplos prácticos, esto desmotiva mucho porque el estudiante piensa ¿Para qué me enseñan esto?.

Esto pasa generalmente con el tema de árboles, te explican los tipos de árboles existen (binarios, múltiples, balanceados, etc) y cómo recorrerlos (pre-orden, post-orden, etc), sin embargo son muy pocos los ejemplos prácticos que he podido encontrar.

El objetivo de este post es mostrar un ejemplo práctico en que se podría hacer uso de los árboles, aunque también se puede hacer uso de otras soluciones.

Tenemos un fichero de texto que contiene en cada linea la edad de personas encuestadas para un estudio de mercado, lo que nos interesa es leer el fichero y mostrar en pantalla la cantidad de repeticiones por cada edad, es decir la edad que más se repiten en la muestra.

Partimos de la premisa que la información no está ordenada y es una muestra de 1000 personas.

Utilizaremos para cada número una estructura (o clase) que almacene el valor de la edad y el valor de la repetición:

public struct elemMuestra
{
public int edad;
public int frecuencia;
}

El algoritmo que podríamos plantear es el siguiente:

1. Leer número del fichero
2. Buscar el número en la estructura ordenada
2.1. Si el número existe en la estructura, incrementar el número de repeticiones
2.1. Si el número NO existe en la estructura, inserto el número en la estructura
3. Cuando termino de leer todos los números del fichero, imprimo cada número con su frecuencia de repetición guardada en la estructura.

En el punto 2 vemos que utilizamos una estructura ordenada, aquí podríamos utilizar un vector, una lista de punteros, un árbol binario,etc. lo importante es que la estructura que definamos nos sea útil tanto para buscar si el número ya existe en la estructura, como para insertar un nuevo número en la estructura.

Si utilizamos un vector, podríamos realizar una búsqueda binaria, debido a que los índices del vector ayudan a este tipo de búsqueda, sin embargo si utilizamos una lista de punteros o un árbol binario tendríamos que usar una búsqueda secuencial, pero sin duda el recorrido en un árbol podría ser mucho más eficiente que el recorrido en una lista de punteros, debido a que la forma del árbol no requiere recorrer a todos sus elementos sino que al tener un orden binario es como si realmente se hiciera una búsqueda binaria, a la búsqueda en un árbol binario se le llama búsqueda Silaina.

El otro punto a evaluar es el tema de la inserción, cuando ya sabemos que el número que hemos leído de fichero aún no existe en la estructura que hemos escogido, utilizar un vector podría ser lo menos eficiente, puesto que si tenemos ordenados 900 números en 20 edades distintas, y llega una nueva edad que es la menor de todas, debemos desplazar todas las edades a una casilla posterior para insertar la nueva edad de forma ordenada, ese desplazamiento puede ser muy costoso. En una lista de punteros la inserción es relativamente sencilla y en un árbol también.

A nivel de programación los árboles son más fáciles de trabajar con funciones recursivas, por tanto si tenéis poca experiencia en funciones recursivas y la cantidad de elementos de la muestra no es tan grande, puede ser más sencillo de programar con vectores + búsqueda binaria o secuencial, sin embargo si veis que la muestra es muy grande y luego vais a realizar muchas búsquedas sobre la estructura cargada y ordenada es mejor trabajarla con árboles binarios + búsqueda sobre árboles binarios.

Ahora bien, para que la búsqueda en un árbol binario sea aun más eficiente deberíamos lograr que el árbol tenga el menor numero de niveles, para esto se realiza lo que se llama el balanceo de árboles:



domingo, 14 de septiembre de 2014

La importancia de pensar

Hace un tiempo hice un ejercicio de memoria para recordar exactamente qué fue lo que me motivó a ser Ing. Informática, la respuesta está en mi colegio, cuando tenia 8 años se introdujo una nueva asignatura que se llamaba "Computación", no era la primera vez que veía un ordenador (ya tenía uno en casa que sólo usaba para jugar), pero era la primera vez que aprendía a usar un ordenador para resolver problemas simples de matemáticas.

El lenguaje que utilizábamos era el Pascal, la clase consistía en leer atentamente un problema y luego dibujar el diagrama de flujo para resolverlo, el profesor pasaba por cada uno de nuestros sitios según íbamos terminando y revisado el diagrama podíamos empezar a programar, nunca antes!! porque lo importante era que se validara que hubiéramos entendido lo que se pedía en el problema y que tengamos claro cómo lo íbamos a enfrentar. Lo más importante de esta clase era la metodología para solucionar los problemas, pensar y luego programar! tuve la suerte de caer en esa clase, fue la base fundamental para decantarme por la rama tecnológica.

Ayer encontré un artículo muy interesante del Blog Tiching en el que plantea los beneficios de que los niños aprendan a programar no solo para fomentar una orientación científica-tecnológica sino para que se acostumbren a estructurar el planteamiento de cualquier tipo de problema. Os recomiendo mucho leer el post, los beneficios de programar con metodología no sólo sirven en un campo puramente técnico sino que sirven para ejercitar y acostumbrarnos a entender los objetivos, plantear estrategias y ejecutar acciones para lograr a cumplir nuestros objetivos.

Cada curso conozco alumnos diferentes, y a pesar de ser de distintos años de carrera, distintas edades e incluso diferentes carreras, todos creen que terminaremos la clase con un programa hecho, en realidad prácticamente no utilizamos el ordenador, lo que se llevan a casa son muchos papeles llenos de pseudocódigo y con las ideas muy claras: Objetivo y Estrategia, esa es la finalidad de la clase, teniendo estas dos cosas, la resolución sale sola.

El próximo post será más técnico sólo quería hacer esta "pausa" por que es parte del aprendizaje entender que antes de programar se debe pensar.


viernes, 1 de agosto de 2014

Más acerca de booleanos

Como ya he comentado en uno de los primeros post Las Variables Booleanas sólo toman dos valores: cierto o falso y su definición depende del lenguaje del programación.

En este post veremos que podemos utilizar variables de tipo entero que cumplan la misma función, asumiendo que sólo podrán tomar dos valores (dos números). En C++ se utilizan con mayor frecuencia los enteros porque el mismo lenguaje asume que el valor 0 hace la función de falso y el valor distinto de 0 hace la función de cierto.

Por ejemplo, el siguiente programa utiliza la variable i para mostrar una cuenta regresiva desde 10 hasta 1 y también sirve como variable de condición de salida para el while:

int main()
{
   //variable i sirve para la impresión y como tope del while
   int i = 10;
   
   //poniendo simplemente la i como condición significa que          
    //iterará mientras la variable i sea diferente de cero
   while(i)
   {
      cout << "i = " << i << endl; 
      i--;//decrementa i hasta que llega a 0 y sale del bucle
   }
   return 0;

}

La salida del programa es esta:

i = 10
i = 9
i = 8
i = 7
i = 6
i = 5
i = 4
i = 3
i = 2
i = 1

Con esto podemos ver que es muy importante conocer las capacidades de cada lenguaje de programación para que se adapten mejor a una solución o a un estilo de programar.

Ahora mostraré otro ejemplo en C++ en el que utilizaré la lógica inversa. El siguiente programa muestra un bucle que itera siempre que la condición No sea igual a 0, para eso se utiliza el símbolo ! que invierte el valor de la variable evaluada:

int main()
{
    //variable i se inicializa con valor 0 (falso)
   int i = 0;
    //Itera mientras el resultado de la parte entera de la división sea igual a 0
   while(!(i/3))
   {
      cout << "i = " << i << endl; 
      i++;
   }
   return 0;
}

La salida del programa es esta:

i = 0
i = 1
i = 2


En conclusión, sabemos que para las instrucciones condicionales y los bucles tenemos la opción de utilizar booleanos, expresiones lógicas y dependiendo del lenguaje de programación también números enteros.