jueves, 29 de agosto de 2013

Sobre condicionales - Actuando en consecuencia

En esta entrada quiero tratar el tema de instrucciones condicionales. Las instrucciones condicionales a la larga son instrucciones que el programador decide si se ejecutan o no en ciertos casos del problema que se está atendiendo. Básicamente uno asigna unas acciones a cada caso posible que se quiera considerar.

Árbol de decisión


Una buena manera de aproximarse a reconocer los escenarios posibles de un problema es armar un árbol de decisión. Un árbol de decisión tiene como objetivo representar los casos y sub- casos asociados a un problema por medio de una gráfica; hay muchos formatos de caso de decisión que van desde los arboles de decisión binarios lógicos (preguntas de si y no), los arboles de decisión probabilísticos (que tienen probabilidades asociadas a los casos) y los arboles de decisión n-arios casuales (que vamos a utilizar en este escrito). Algunas definiciones

Un nodo de un árbol de decisión es un caso: Suena obvio ¿cierto?, pues NO lo es. Normalmente estamos tentados a escribir en los nodos cosas que no son casos del problema, sino consecuencias o soluciones al caso. También se suele etiquetar el caso con un número para distinguirlo.


.................
|               |
| Caso 1        |
|               |
|...............|

Un nodo puede tener un padre: 

Si un nodo tiene un padre, significa que el nodo es un sub-caso de su padre. Se puede afirmar que si el caso hijo se presenta es porque el caso de su padre también se presentó. La relación entre estos casos es un AND (sucedieron los dos).
 .................
 |               |
 | Caso 1        |
 |               |
 |...............|
         |
         |
         |
       \ | /
        \./
 .................
 |               |
 | Caso 1.1      |
 |               |
 |...............|


Un nodo puede tener un hermano: 

Si un nodo padre tiene dos o más nodos hijos (sub-casos), estos nodos hijos se consideran hermanos. En un árbol de decisión se consideran casos hermanos mutuamente excluyentes, o sea relacionados por un XOR (sucede uno o el otro, nunca más de uno). Dicho de otra manera, no pueden suceder estos dos casos al tiempo.

 .................
 |               |
 | Caso 1        |
 |               |
 |...............|
         |--._
         |    `-..
         |        ``-._
       \ | /           `-.._    .
        \./                 `-._|
 .................     ......::::.......
 |               |     |               |
 | Caso 1.1      |     | Caso 1.2      |
 |               |     |               |
 |...............|     |...............|

El nodo raíz (que no tiene padre) se considera como el escenario general de todo el problema.

¿Para qué sirve esto?

Diversas situaciones requieren tener en cuenta todos los casos posibles, especialmente para poder tomar una determinación ante ellos. Los problemas que valen la pena, tienen un número de casos que no es manejable a menos que se tenga un modelo de representación y por eso es útil un árbol de decisión. A la hora de programarlo, se hace más importante aún, porque el arbol se reflejará en nuestra estructura de condicionales.

El condicional en JAVA - IF


La instrucción para la programación de condicionales en JAVA se llama IF y permite especificar la estructura de casos y las instrucciones a ejecutar en cada uno de ellos. La primera forma estructural del if es similar a lo siguiente:

if (condicion_caso)
{
   //instrucciones
   operacion();
}

El paréntesis encierra una expresión que evalúa un valor booleano (true si el caso se da, false si no). Luego va un agrupador (corchetes) con las instrucciones que se ejecutarán en ese caso. El funcionamiento es simple, si la condición se da, se llama al método operacion; en caso contrario no sucede nada.

La siguiente forma estructural utiliza la cláusula ELSE

if(condicion_caso)
{
    operacion1();
}
else
{
    operacion2();
}

En este caso, de darse la condición se llamará al método "operacion1", en caso contrario (si la condición no se cumple) se invocará el método "operación2". La cláusula ELSE funciona en este caso como decir "todos los otros casos" Esto se asemeja a un arbol de decisión así:

    .................
    |               |
    | Problema      |
    |               |
    |...............|
            |--._
            |    `-..
            |        ``-._
          \ | /           `-.._    .
           \./                 `-._|
    .................     ......::::.......
    |               |     |               |
    | Caso 1        |     | En cualquier  |
    |               |     | otro caso     |
    |...............|     |...............|



A una claúsula else se le puede adicionar también un if, de modo que se pueden especificar varios casos hermanos de manera puntual. Por ejemplo:

if(caso1)
{
    operacion1();
}
else if(caso2)
{
    operacion2();
}
else if(caso3)
{
    operacion3();
}
else
{
    operacion4();
}


Aquí se especifican cuatro casos diferentes y una operación diferente para cada uno de ellos. También se puede usar el else con esta estructura, haciendo que si ninguno de los casos se da (los casos 1,2 y 3) entonces se ejecutará la operación 4. Esto se asemeja a un árbol de decisión como este:

     .................
     |               |
     | Problema      |
     |               |
     |...............|
           /`-.._
          /   \ `:--.._
         /     `.  `-..``-.._
        /        \     `-._  `'--.__
       '          `.       `-._     `--.._
  +-------+    +-------+  +----`--+  +----'--+
  |       |    |       |  |       |  |       |
  | Caso1 |    | Caso2 |  | Caso3 |  | Otros |
  |       |    |       |  |       |  |       |
  +-------+    +-------+  +-------+  +-------+


Sin embargo, el if es una estructura jerárquica del lenguaje, por ello es posible que dentro de la operación de cada uno de los casos se ponga otra estructura if (anidar un if dentro de otro). El equivalente a esta operación es crear hijos en los arboles de decisión. Por ejemplo:

if(caso1)
{
    if(caso11)
    {
        operacion11();
    }
    else if(caso12)
    {
        operacion12();
    }
}
else if(caso2)
{
    if(caso21)
    {
        operacion21();
    }
    else
    {
        operacion22();
    }
}
else if(caso3)
{
    if(caso31)
    {
        if(caso311)
        {
            operacion311();
        }
        else if (caso312)
        {
            operacion312();
        }
    }
    else if(caso32)
    {
        operacion12();
    }
}
else
{
    operacion4();
}

Nótese que en esta estructura if hay varios casos anidados unos dentro de otros. El anterior ejemplo sería similar a evaluar un árbol de decisión similar al siguiente:

     .................
     |               |
     | Problema      |
     |               |
     |......`........|
          .'   `-:`---..___
         /        `-.   ``-`:-==...__
        /            `-.        ``-..ii`--...__
      .'                `-.            ``--.._ ```--...__
     /                     `-.                ``--..__   ```--...__
  +-------+                 +-`-.---+             +---``--+   +----'--+
  |       |                 |       |             |       |   |       |
  | Caso1 |                 | Caso2 |             | Caso3 |   | Otros |
  |       |                 |       |             |       |   |       |
  +---+`-._                 +---+`.-+             +--.'`.-+   +-------+
       |   `-.                .'   `-.              /    `.
  +----+--+ +-`-.=--+    +---+---+ +--`.---+  +---.'--+ +--`.---+
  |       | |       |    |       | |       |  |       | |       |
  | Caso11| | Caso12|    | Caso21| | Otros |  | Caso31| | Caso32|
  |       | |       |    |       | |       |  |       | |       |
  +-------+ +-------+    +-------+ +-------+  +---\---+ +-------+
                                                .' `.
                                               /     \
                                         +----+--+ +--`.---+
                                         |       | |       |
                                         |Caso311| |Caso312|
                                         |       | |       |
                                         +-------+ +-------+

Espero que este post les sea de utilidad!

miércoles, 14 de agosto de 2013

Variables y Operadores en JAVA

Volviendo a las bases de JAVA, este post está dedicado a la declaración de variables, a su uso, a los operadores que se pueden usar y demás detallitos importantes al iniciar con JAVA.

Noción de Variable


En general una variable en un espacio en memoria que reservamos para guardar un valor específico. Podemos imaginar una variable como una caja donde guardamos valores, la cual tiene un nombre y además un tipo (el tipo de dato que queremos almacenar). Así, se esperaría que podamos almacenar números, letras, palabras, valores booleanos y otros cuantos más. 

   calificacion
  |-----------.
  |           |
  |    5.0    |
  |           |
  |...........|


En resumen, tenemos que una variable tiene un nombre, un tipo y un valor. Es de esperar que el valor sea del tipo que la variable es y también es de esperar que no se puedan crear variables con el mismo nombre, porque Java se confundiría si no fuera así.

Algunos tipos comunes en java


Crear una variable implica definir de que tipo será. En java se permite el uso de ciertos tipos básicos que nos permiten representar:

- Números
- Caractéres
- Booleanos
- Cadenas de Caracteres
- Bytes

Estos no son los únicos tipos de variable que Java permite, de hecho el lenguaje permite la definición de tipos de parte del usuario (clases). Sin embargo en este post nos dedicaremos a los tipos más básicos.

Tipos

Java puede representar valores booleanos, números enteros, números reales, caracteres y cadenas de caracteres, sin embargo tienen algunas limitaciones dependiendo del tipo que se use. Algunos de los tipos que java permite utilizar son:

  • int (entero): Permite representar números enteros desde -2,147,483,648 hasta 2,147,483,647
  • short (entero pequeño): Permite representar números enteros desde -32,768 hasta 32,767
  • long (entero grande): Permite representar números enteros desde -9,223,372,036,854,775,808 hasta 9,223,372,036,854,775,807
  • float (número en notación de punto flotante): Permite representar números reales (con cifras decimales) con una precisión de 32 bits. El rango de valores posibles es un tema que no trataré aquí.
  • double (número en notación de punto flotante): Permite representar números reales (con cifras decimales) con una precisión de 64 bits. El rango de valores posibles es un tema que no trataré aquí.
  • boolean (valor booleano): Permite representar valores verdadero (true) o falso (false)
  • char (caractér): Permite representar caracteres según la codificación Unicode de 16 bits (UTF-16) con una capacidad de 65,535 caracteres (suficientes para representar los letras, números, simbolos especiales y bastantes caracteres de idiomas como el chino, árabe y demás que no usan el alfabeto inglés)
  • String (cadena de caracteres): Permite representar cadenas de caracteres, el tamaño máximo es dependiente de la cantidad de memoria RAM de la máquina donde se ejecuta, en la práctica suficiente para la mayoría de usos.
Declaración (Creación de una variable)

Para crear una variable en Java, simplemente se debe especificar el tipo y el nombre de la variable a crear seguidos de un punto y coma (;).

int entero;
short enteroPeque;
String cadena;
float calificacion;
double numeroDecimal;
long numeroGrandote;

En todo caso, la idea es que el nombre de una variable nos diga algo acerca de su significado. Si estamos programando un sistema de calificaciones, probablemente nombremos una variable "nota", entre más claros sean nuestros nombres de variables, más fácil será comprender el programa que estamos haciendo.

Asignación (Cambiar valor de una variable)


Las variables no sirven de mucho si no podemos manipular sus valores, por eso existe un operador que nos permite hacer precisamente eso. Sin más , aquí unos ejemplos:


int i; //creo una variable entera
i=0; //le cambio el valor a cero
short s = 0; //en la misma línea creo una variable entera y le asigno el valor de cero
char caracter = 'c'; //creo una variable caracter y le doy valor con la letra 'c' , nótese el uso de las comillas simples
String palabra = ""; //creo una variable cadena de caracteres y le doy valor vacío, nótese el uso de las comillas dobles
String otraPalabra = "Hola!"; //creo una variable cadena y le doy el valor "Hola!"
float f = 4.0;
boolean valor = true;
double d = 4.0;
double d2 = d;

El operador de asignación requiere de una variable creada, también se puede hacer que una variable cambie de valor al valor de otra variable, como se hace en las últimas líneas del ejemplo. En general, a la izquierda del operador de asignación (=) debería estar la variable a modificar y a la derecha el valor que se le asignará.


Operadores

Los operadores se utilizan para hacer cálculos con los valores de las variables, más ampliamente, sirven para intervenir sobre ellas. Los operadores matemáticos básicos son funcionales de una manera muy similar al uso que se les da en el álgebra. Sobre los operadores es importante recordar que tienen unas entradas (dominio) y unas salidas (rango). Ahora algunos ejemplos:


int i =0; // creo un entero, le asigno cero
int j =2; // creo un entero, le asigno dos
int suma = i+j; //creo un entero, le asigno la suma de i y j, o sea 2
int resta = 10-j;//creo un entero, le asigno la resta de 10 y j, o sea 8
int multiplicacion = 2*2; //creo un entero, le asigno la multiplicación de 2 y 2, o sea 4
int division = 4/2; //creo un entero, le asigno la división de 4 y 3, o sea 4
int modulo = 11 % 2; //creo un entero , le asigno el módulo entre 11 y 2, o sea 1

Aparte de las operaciones típicas, aparecen algunas otras que funcionan con números:


int i =0; //creo un entero con valor cero
i++; //aumento el entero en uno , o sea queda con valor 1
i+=2;//aumento el entero en dos, o sea queda con valor 3
i = i+3; //aumento el entero en tres, o sea queda con valor 6
i*= 2; //multiplico el entero por dos, o sea queda con valor 12
i/=2; //divido el entero en dos, o sea queda con valor 6
i--; //decremento el entero en uno, o sea queda con valor 5
i-=2; //decremento el entero en dos, o sea queda con valor 3

Las operaciones anteriores aplican con números decimales, con excepción del módulo (debido a que su resultado es entero). En el caso de la división, se considera siempre una división decimal, para divisiones enteras se requiere interpretar el resultado de esta división como un entero, obviando su parte decimal.

También las operaciones booleanas básicas se pueden realizar en java:

boolean valor = true; //creo un booleano y le asigno el valor verdadero
boolean otro = false; //creo un booleano y le asigno el valor falso
boolean andlogico = valor && otro; //creo un booleano y le asigno el resultado de la operacion AND 
boolean orlogico = valor || otro; //creo un booleano y le asigno el resultado de la operacion OR  
boolean negacion = !valor; //creo un booleano y le asigno el resultado de la operacion NOT

Los Strings soportan el operador + para efectos de concatenar cadenas con otros tipos o entre ellas:

 int i = 2; 
 String cadena = "ho"+"la"+" "; //queda con valor "hola"
 String cadena2 = cadena + "mundo"; //queda con valor "hola mundo"
 String cadena3 = cadena2 +" "+ i; // queda con valor "hola mundo 2"



Los operadores relacionales también funcionan en java, aquí unos ejemplos:

boolean iguales = 2==2; //queda en true, porque 2 es igual a 2
boolean diferentes = 2!=2; //queda en false, porque 2 NO es diferente de 2
boolean mayor = 2>3; //queda en false porque 2 NO es mayor que 3
boolean mayorigual = 2>=3; //queda en false porque 2 NO es mayor o igual a 3
boolean menor = 2<=3 //queda en false porque 2 es menor o igual a 2



Ojalá sea una buena introducción ... Hasta un próximo post :)