Java desde Cero – Estructura de Control de Flujo – Bucles 1

Cuando hacemos uso de un programa existen o lo creamos podemos observar que existen operaciones que se han de repetir, por ejemplo imaginemos que nuestro programa tenga que imprimir en consola el conteo de 1 a 100, si lo hiciéramos de forma convencional tendríamos que:

System.out.println(1);
System.out.println(2);
System.out.println(3);
System.out.println(4);
...

Esto resulta ineficiente, al ser una operación que es repetitiva debería poder realizarse a través de una estructura de control de flujo, y estos son los bucles, los bucles también llamados iteradores,  permiten la ejecución reiterada de una sentencia o sentencias, ahora bien, los bucles pueden ser de dos tipos, determinados o indeterminados, los determinados se definen básicamente porque conocemos cuántas veces se han de repetir las operaciones (o cuántas iteraciones se han de realizar) mientras que los indeterminados no sabemos con exactitud cuando han de finalizar ya que dependen de una condición booleana (verdadera o falsa) para continuar o finalizar su ejecución. Java posee 3 estructuras de control de flujo de ejecución que son bucles, estos son la estructura For, While y Do While, vamos realizar una revisión del bucle “For“.

La estructura de un bucle for es la siguiente:

for (inicialización; finalización; incremento) {
       sentencias
}
  • Inicialización: Es una expresión, es de donde ha de iniciar el conteo de las iteraciones, podemos utilizar una variable que ya ha sido declarada e inicializada previamente o podemos hacerlo en esta sección (por ejemplo iniciamos en que a=1).
  • Finalización: Es una expresión booleana que se evalúa su condición de verdadero o falso en cada iteración en función a sí el valor de inicialización ya alcanzó un valor final específico (por ejemplo evaluamos si a <100) y mientras no se haya alcanzado dicho valor las iteraciones han de continuar.
  • Incremento: Es invocada luego de la iteración y posterior evaluación, aquí se define la política de incremento del valor de inicialización (por ejemplo hacemos que a tenga un incremento de 1 en 1 o podemos hacer que vaya de 2 en 2).

Nuestro anterior objetivo era el conteo del 1 al 100, entonces esto en la estructura “for” se implementa de la siguiente forma:

for (int i=0;i<100;i++) {
       System.out.println(i);
}

Observemos que hemos declarado e inicializado la variable “i” en la estructura, recuerda que en java siempre debes definir el tipo de dato, un aspecto a tomar en cuenta es que  el alcance de la variable “i” solo estará circunscrito a la estructura y no existe más allá de ella, sé que aún no hemos hablado del alcance de una variable, pero por el momento piensa en el alcance como la existencia de una variable en un programa, con esto quiero decir que fuera de la estructura la variable “i” no existe y podría marcar un error si es que pretendemos hacer uso de ella. Luego definimos que su terminación será cuando “i” ya no cumpla la condición de ser menor que 100, finalmente establecemos la política de incremento que está dada por un aumento de “i” de 1 en 1.

Ahora bien, en Java la inicialización donde se define la variable, no se puede hacer por fuera, si no nosotros hiciéramos:

int i=0;
for ( i; i < 10; i++) {
   System.out.println(i);
}

Esto nos marcaría un error; para poder hacer uso de una variable que ya está definida, lo que debemos de hacer es dejar el espacio en blanco de su inicialización:

int i=0;
for ( ; i < 10; i++) {
   System.out.println(i);
}

Hecha esta salvedad, veamos un ejemplo en el que le preguntamos al usuario que se ejecute un conteo la cantidad de veces que el desee  y que el defina la política de incremento.

import java.util.Scanner;

public class Bucles_1 {

public static void main(String[] args) {
  Scanner entrada=new Scanner(System.in);

  System.out.println("¿Cuántos números desea contar?");
  int cantidad=entrada.nextInt();
  System.out.println("¿Desde dónde desea iniciar?");
  int inicio=entrada.nextInt();
  System.out.println("¿De cuánto en cuánto desea avanzar?");
  int avance=entrada.nextInt();
  entrada.close();

  for(int i=0;i<cantidad;i++) {
    System.out.println(inicio);
    inicio+=avance;
  }
  }

}

¿Qué hemos hecho?, hemos importado la clase Scanner y hemos hecho las consultas a través de la consola, cada entrada de teclado que responde a las preguntas, se almacena en las variables, nos interesa exactamente la cantidad de veces que se ha de iterar, el cómo se avance y de donde se inicie es casi por así decirlo que será implementado mediante un artilugio, luego de ello implementamos la estructura FOR hacemos que se imprima el primer valor que se ha registrado y luego hacemos que se incremente en función del avance que se ha requerido.

El bucle FOR también puede ser usado con cadenas de texto, es decir las cadenas de texto pueden ser iterados a través  de su longitud, la longitud está determinada por la cantidad de caracteres o espacios en blanco que existan dentro de las comillas que la contienen, por ejemplo: “hola” tiene una longitud de 4 ya que son 4 caracteres que la componen, para determinar la longitud de una cadena que es de la Clase String, debemos hacer uso de uno de sus métodos que se define como length. Entonces podemos implementarlo de la siguiente forma, vamos a hacer que se imprima cada uno de los caracteres de la cadena “hola”, para ello también haremos uso del método chartA(index) que nos devuelve el caracter del indice indicado, debemos saber que la cadena si bien es cierto su longitud es de 4 su índice siempre inicia desde el valor cero, es decir que el índice está yendo de 0 a 3 tal que el índice 0 es “h”, 1 es “o” y así sucesivamente, entonces:

String saludo="hola";
for(int i=0;i<saludo.length();i++) {
    System.out.println(saludo.charAt(i));
}

De este modo, hemos visto las posibilidades del uso del bucle for.

 

 

 

 

Anuncios

Programación Dinámica Determinística – Investigación de Operaciones

Anteriormente:

Programación Dinámica

Ya hemos revisado las bases de la programación dinámica, una técnica empleada dentro de la ingeniería industrial para la optimización de procesos. Ahora vamos revisar la programación dinámica determinística, que se centra en los problemas que el estado de la siguiente etapa está determinado por completo por el estado y la política de la etapa actual. La programación determinística se puede describir por el siguiente gráfica:

pd_13

Siendo esto el único factor diferencial, todo el proceso y características posteriores se cumplen, entonces veamos algunos ejemplos de su aplicación para entender  bien su entendimiento.

AGENCIA DE PUBLICIDAD

Una agencia de publicidad está llevando a cabo una campaña de publicidad de una semana para una tienda local. La agencia ha determinado que es posible que la campaña más efectiva pudiera incluir la colocación de anuncios en cuatro medios. Un periódico diario, un periódico dominical, radio y televisión. Se tiene disponible $ 8,000 para esta campaña y la agencia le gustaría distribuir esa cantidad en incrementos de $1,000 en todos los medios, de manera que se maximice el índice de exposición a la publicidad. Investigaciones llevadas a cabo por la agencia permiten obtener las siguientes estimaciones de la exposición por cada $1,000 de gasto en cada uno de los medios.

pd_14

Determine cuánto debe invertir la agencia en cada uno de los medios para maximizar la exposición de los anuncios.

Resolución

Vemos que no se menciona de forma explícita las etapas, para ello debemos de ponernos a meditar sobre la toma decisión que ha de ocurrir, podemos decir que si empezamos de arriba hacia abajo en la tabla que se ha presentado, podríamos decir que nuestro estado inicial es S1=$8000 y tomaremos una decisión X1 que sería el valor de la inversión en uno de los medios informativos tal que nos lleve al  siguiente estado (el siguiente medio informativo) y en el siguiente estado tendremos S2=8000-X1, y así sucesivamente S3=S2-X2 tal que al final quedemos con 0, observemos la situación determinística que dicta que el estado n+1 está determinado enteramente por el estado anterior y su contribución , ahora bien bajo esto podríamos decir que los medios informativos son las etapas, ya que en ellos se concentran las tomas de decisiones, podemos entonces realizar la siguiente tabla:

pd_15.png

Sí cada etapa es cada medio informativo, observemos lo que sucede en la etapa 1; en la etapa 1 podemos elegir, invertir 0 a 8, no sabemos cuánto pero sabemos que esas son los valores posibles:

pd_16

Si encontramos una razón a ello podemos decir, sea:

  • Sea n el número de estados que es 4.
  • Sn: La cantidad actual para invertir en la etapa n
  • xn: La cantidad actual que se invertirá en la etapa n tal que xn ={0,1,2,3,4,5,6,7,8}
  • Sn+1: La cantidad restante de inversión en la etapa n+1.
  • Csn,sn+1: la contribución inmediata de ir de n a n+1.

Entonces:

Sn+1=Sn-Xn

Con estos datos podemos buscar la relación recursiva; nuestro objetivo es obtener la maximización de exposición en los medios, entonces en cada etapa se ha evaluado y obtenido un gn+1(Sn+1), entonces podemos decir que la maximización de exposición (Gmax) es:

Gmax=C(Sn,Sn+1)+gn+1(Sn+1) equivalente a C(Sn,Sn+1)+gn+1(Sn-Xn)

Entonces en la etapa 1, sea S1=8, si invertimos los 8 el cual es nuestro X1  en la etapa 1 nos quedamos con 0 el cual es nuestro Sn+1.Y la contribución de invertir esos 8 en la etapa 1 es  C(Sn,Sn+1)=82, según dato en tabla.Esto ocurre de forma similar con los otros posibles estados. Bajo esa misma lógica creamos los demás estados y etapas.

pd_17.png

Obtenemos una red de la forma en que se muestra, expliquemos un poco que es lo que está sucediendo:

pd_18

En la primera etapa el esfuerzo de invertir 6 trae como resultado 80 y me quedo con 2 que es el estado inicial para la etapa 2, luego yo puedo decidir invertir esos 2 en la etapa 2 y me quedaría con 0, el resultado de invertir 2 en la segunda etapa es 55, o podría solo invertir 1 y el resultado de invertir 1 es 15 en la segunda etapa o no podría invertir nada, quedándome con 2 y el resultado de no invertir nada es 0, no puedo invertir 3 ya que no poseo dicha cantidad, siguiendo esa misma lógica es que se ha desplegado toda la red. En la última etapa sea lo que quede se va a distribuir, entonces el último estado será de valor 0, con todo esto descrito pasamos a las tablas:

Etapa 4 (Televisión):

pd_19

Etapa 3 (Radio):

pd_20

 

Etapa 2 (Periódico dominical:

pd_21

Etapa 1 (Periódico diario):

Aquí se observa lo sucedido con el nodo de 8 a 2, existe una inversión 6, invertir 6 en la etapa 1 trae una exposición de 80, dejando esos 2 para la siguiente etapa, es ahí donde revisamos cual es la forma más óptima de distribuir 2, que en la siguiente etapa es de 40

pd_22

Finalmente la mayor exposición es de 167 y se logra a través de la distribución de inversión de:

2->3->1->2 o 2->2->1->3

Aquí nos detenemos por el momento, esperando que les haya sido de su agrado.

Programación Dinámica – Investigación de Operaciones

TEORÍA

La programación dinámica es un técnica muy útil para la toma de decisiones de problemas complejos que pueden ser discretizados y secuencializados, proporciona un procedimiento sistemático para determinar la combinación óptima de decisiones; este método no tiene una formulación estándar ya que las ecuaciones que se planteen responden al problema específico.

Esta técnica es muy útil para la reducción el tiempo de ejecución de un algoritmo mediante la utilización de subproblemas y subestructuras óptimas, todo ello haciendo uso del principio de optimalidad:

“Dada una secuencia óptima de decisiones, toda subsecuencia de ella es, a su vez, óptima”

Para la introducción de las características y terminologías de la programación dinámica se creo el problema de la diligencia por parte del profesor Harvey M Wagner:

Un cazafortunas mítico de Missouri que decide ir al oeste a sumergirse en la fiebre del oro que surgió en California a mediados del siglo XIX. Tiene que hacer ek viaje en diligencia a través de territorios sin ley, donde existen serios peligros de ser atacado por merodeadores. A pesar de que su punto de partida y su destino son fijos, tiene muchas opciones en cuanto a qué estados – o territorios – debe elegir como puntos intermedios (tomar en cuenta que el viaje es siempre de izquierda a derecha).

planos1

Para lograr el viaje se requieren de 4 etapas para ir desde el estado A a su destino en el estado J, este cazafortunas es un hombre prudente preocupado por su seguridad. Después de reflexionar un poco ideó una manera bastante ingeniosa para determinar la ruta más segura. Se ofrecen pólizas de seguros de vida a los pasajeros. Como el costo de la póliza de cualquier jornada en la diligencia está basado en una evaluación cuidadosa de la seguridad del recorrida, la ruta más segura debe ser aquella cuya póliza represente el menor costo total“.

El costo de la póliza estándar del viaje en diligencia, del estado i al estado j se denota como:

costo

Creamos las tablas asociadas a cada uno de estos costos tenemos:

pd_1

FORMULACIÓN

Vamos a dividir el problema en etapas, las etapas serán evaluadas en la solución más óptima y se agregarán en el análisis de la siguiente etapa hasta encontrar la solución más óptima general, entonces:

Sean Xn=El destino inmediato de la etapa n (el n-ésimo viaje que se hará en diligencia), tal que la ruta seleccionada final para este problema será:

A->X1->X2->X3->X4, donde X4=J

Sea fn(s,xn) el costo total de la mejor política global para enfrentar las etapas restantes, mientras el cazafortunas se encuentra en el estado “s“, listo para iniciar la etapa n y elige xn como destino inmediato. Dados s y n, sea yn el valor de xn – no necesariamente único – que minimiza fn(s,xn) y gn(s) el valor mínimo correspondiente de fn(s,xn). Entonces:

gn(s)=mínf(s,xn)=f(s,yn)

Donde:

 fn(s,xn)=costo inmediato (etapa n) + costo futuro mínimo (etapas n+1 en adelante)

Representado de la siguiente forma:

pd_3

Entonces nuestro objetivo es encontrar g1(A) y su ruta correspondiente, pero para encontrarlo debemos de resolver de forma sucesiva g4(s), g3(s), g2(s) para cada uno de los estados posibles de s y usar g2(s) para encontrar g1(A).  Entonces es una suerte de ir del fin hacia el inicio obteniendo el costo mínimo (para este caso) en cada una de las etapas.

PROCEDIMIENTO

Recordemos que hemos de ir de adelante hacia atrás, entonces partimos de la última etapa, en nuestro ejemplo esta es la etapa 4, la cual es la última etapa por recorrer, cuando esto sucede su ruta está perfectamente definida, sea por H o por I la ruta se dirige hacia J:

pd_1

Podemos entonces hacer lo siguiente:

pd_2

para el caso de la etapa 4 no existe un g(S) ya que es la última, podemos considerar entonces que este es cero, así que está solo calculado directamente con el costo que representa ir por cada una de las rutas H o I hacia J.

En la etapa 3 se nos representa que ahora se deben de recorrer 2 etapas, aquí debemos de tomar especial atención a lo siguiente; tomemos como base que desde F podemos ir sea hacia H o I:

pd_4

Sabemos por la resolución anterior que el g4(H)=3 y g4(I)=4, entonces si decidimos ir de F->H tendríamos un costo total 6+3=9, ya que estamos haciendo la ruta F->H->J; si hacemos F-I tendríamos un costo total de 3+4=7 siendo menor el costo  y haciendo que g3(F)=7, lo mismo ocurre con E y G, en consecuencia realizando la tabla:

pd_5

En cada una de las celdas hemos hecho la operación descrita para F.

Al resolver la segunda etapa, realizamos los mismos cálculos donde obtenemos la siguiente tabla:

pd_6

Solo podemos observar que el destino inmediato de B y D pueden ser E o F ya que minimizan el valor de “g“, ahora debemos de ocuparnos de la primera etapa:

pd_7

Resuelta la última etapa ya nos es  posible obtener la ruta o rutas óptimas para nuestro problema, tenemos entonces que:

A->C->E->H->J  o  A->D->E->H->J o A->D->F->I->J

Tenemos estas 3 rutas disponibles donde todas tienen el costo mínimo de 11.

CARACTERÍSTICAS

  • El problema se puede dividir por etapas, y cada una de las etapas requiere de una política de decisión.
  • Cada etapa tiene cierto número de estados, los estados son las distintas condiciones posibles en las que se puede encontrar el sistema en cada etapa.
  • El efecto de la política de decisión en cada etapa es transformar el estado actual en un estado asociado con el inicio de la siguiente etapa, entonces podemos decir que un estado es una columna de nodos, cada nodo representa un estado y cada rama una política de decisión.
  • El procedimiento de resolución de la programación dinámica está diseñado para encontrar una política óptima para cada etapa logrando así la política óptima general.
  • Dado el estado actual, una política óptima para las etapas restantes es independiente de la política adoptada en etapas anteriores, por ende la decisión inmediata óptima solo depende del estado actual y no de cómo se llegó ahí, esto es el principio de optimalidad de la programación dinámica.
  • Se dispone de una relación recursiva que identifica la política óptima para la etapa n, dada la política óptima para la etapa n+1. La relación recursiva en su forma difiere de un problema a otro, pero se puede emplear las notaciones que ya se han usado en el problema de la diligencia, tal que finalmente siempre será cuestión de hallar un máximo o un mínimo.
  • Cuando se hace uso de la relación recursiva, el procedimiento de solución comienza al final se mueve hacia atrás etapa por etapa hasta encontrar la política óptima en cada etapa hasta la etapa inicial.

APLICACIONES

La Ruta Crítica

Hallar la ruta crítica con programación dinámica:

pd_8

Resolución

Recordemos que debemos de dividir por etapas nuestra red, sabemos que una etapa es un conjunto nodos o estados y los arcos con su valor con las contribuciones directas; podemos notar que existe la particularidad que el estado 3 está conectado tanto al estado final como a un estado intermedio, este estado 3 en consecuencia estará presente en al menos 2 etapas, si hiciéramos un corte podríamos observar que nuestro problema puede segmentarse en 4 etapas, entonces:

La etapa 4 es la última etapa por recorrer, y el único estado siguiente es 7 y no existe un g5 por lo cual el esfuerzo o tiempo será solo la contribución directa del arco:

 

pd_9

La etapa 3, ahora son dos etapas que debemos de considerar, en este caso en particular, recuerda tomar en cuenta todos los estados previos que te permiten llegar a los estados futuros, para ello observemos el estado 4 que si bien se encuentra en un estado final para la etapa también es parte del estado actual ya que conecta con otros estados finales, se puede observar que por ejemplo en el estado 2 que va hacia el estado 6 y 4 la contribución inmediata de ir de 2 a 6 más el g4(6)  máximo que hasta el momento es 1, igualmente ocurre de 2 a 4, de entre ellos dos elegimos el máximo, ¿Por qué el máximo?, pues porqué estamos buscando la ruta crítica, la ruta crítica está representada por la ruta más larga que me permita ir de 1 a 7.

pd_10

La etapa 2, correspondería ubicar en los estados finales a 1, 2, 4 y 5, pero no tiene sentido colocar a 1 ya que no existe un estado previo para él, entonces solo colocaremos a 2, 4 y 5, hacemos la misma lógica que ya está sustentada en la relación recursiva que aplica para este caso:

pd_11

La etapa 1 y última etapa se consideran las 3 etapas anteriores, entonces tenemos:

pd_12

Hecho todo ello, ¿Cómo hallamos la ruta crítica?, debemos de seleccionar todos los valores máximos, si existe un estado (nodo) que se encuentre en más de una etapa debemos de seleccionar en cual de ellos es el mayor por ejemplo, revisemos la ruta de 1 a 2 y 1 a 3 aparentemente sea de 1 a 3 la ruta más larga; sin embargo, la ruta de 1 a 2 en la etapa 2  es mayor que la ruta de 1 a 3, es por ello que tomamos dicha ruta, luego, en la etapa 2 se obtuvo ese valor del estado 2 ya que provenía del estado 6 y de 6 a 7, entonces nuestra ruta crítica será:

1->2->6->7 longitud de 13

 

Java desde Cero -Estructura de Control de Flujo – Switch

Vamos a revisar el segundo condicional de Java, y este es Switch el cual es una estructura de selección múltiple, la siguiente figura ilustra el ejemplo perfectamente:

switch-statement-flowchart

Si lo observamos con detenimiento veremos que es una expresión de condicionales anidados o que al menos puede ser reemplazado por ellos haciendo que su eso no sea de carácter obligatorio cuando nos encontramos en una situación similar, esto provoca que su uso sea menos; sin embargo, como una estructura del lenguaje es necesario explicarlo ya que aunque pueda ser sustituida en ocasiones brinda claridad en la implementación del código. Esta estructura de control de flujo solo puede evaluar números enteros o todo aquello que se pueda llevar a un entero; es decir, no puede evaluar cadenas, sí puede evaluar caracteres y los tipos enumerados, también sufre la restricción de solo evaluar valores concretos de la expresión y no intervalos  ni expresiones compuestas.

La estructura de Switch, está compuesta por múltiples “case” que es donde se evalúa la condición, un break por cada case que detendrá el flujo por switch y retornará al flujo anterior con las sentencias ya ejecutadas dentro de la opción que ha cumplido la condición y por un default que es opcional y que se ejecutará cuando no haya opción alguna que cumpla la condición.

“Hagamos un ejemplo de su uso, por ejemplo una calculadora ( aunque solo itere una sola vez), le pediremos al usuario que ingrese una opción a través de la consola tal que si es 1 entonces será una suma, 2 será una resta, 3 multiplicación y  4 una división“.

Para la entrada de datos por teclado debemos de hacer uso de la clase Scanner y crear una instancia del mismo, luego debemos de realizar la consulta creando un menú tal como se ha propuesto, además de ello debemos de crear dos variable de tipo double (ya que nuestra división podría terminar en decimales y no se podría realizar un adecuado tratamiento de formato con los tipo int), entonces:

package clases;
import java.util.Scanner;
public class Control_switch {

public static void main(String[] args) {
  Scanner entrada= new Scanner(System.in);
  System.out.println("Ingrese una operación \n 1 -Suma \n 2 -Resta \n 3 -Multiplicación \n 4 -División");
  int opcion=entrada.nextInt();
  double a, b;
  //continua con el switch
}}

Observamos que solo hemos declarado las variables a y b mas no las hemos inicializado, recordemos que según el flujo de ejecución toda variable debe ser declarada e inicializada antes de su uso, así que debemos de asegurarnos de ello para no tener saltos de error, otro aspecto a observar es el uso de “\n” esta sintaxis produce un salto de línea, y nos resulta útil en lugar de estar usando demasiados println.

Ahora debemos de implementar la estructura switch, recordemos que la condición (que será un número entero) recorrerá cada uno de los casos hasta encajar en alguno de ellos, en todas las operaciones (suma, resta, multiplicación y división) haremos uso de la instancia de Scanner para solicitar el ingreso de datos y almacenarlos en las variables a y b respectivamente; y al momento de la impresión es donde hemos de ejecutar la operación que se haya elegido; sin embargo, existe un detalle con la opción de división, en la división nosotros vamos a restringir que la salida del resultado sea a dos decimales, ya que podríamos tener un periódico puro o un número bastante inmenso en la parte decimal y no sería elegante, para poder redondear la salida del dato debemos darle formato, entonces debemos de usar el método printf que ya hemos visto anteriormente, entonces tenemos:

switch (opcion) {
  case 1:
     System.out.println("Ingrese el primer número");
     a=entrada.nextInt();
     System.out.println("Ingrese el segundo número");
     b=entrada.nextInt();
     System.out.println("La suma es: "+(a+b));
     break;
 case 2:
     System.out.println("Ingrese el primer número");
     a=entrada.nextInt();
     System.out.println("Ingrese el segundo número");
     b=entrada.nextInt();
     System.out.println("La resta es: "+(a-b));
     break;
  case 3:
     System.out.println("Ingrese el primer número");
     a=entrada.nextInt();
     System.out.println("Ingrese el segundo número");
     b=entrada.nextInt();
     System.out.println("La multiplicación es: "+(a+b));
     break;
  case 4:
     System.out.println("Ingrese el primer número");
     a=entrada.nextInt();
     System.out.println("Ingrese el segundo número");
     b=entrada.nextInt();
     System.out.print("La división es: ");;
     System.out.printf("%1.2f",(a/b));
     break;
  default:
     System.out.println("No ha elegido alguna de las opciones válidas");
     break;
}
entrada.close();

Luego de finalizada las operaciones no olvidemos en cerrar el flujo de datos, nota en la división que hemos dado el formato adecuado a dos decimales, pero para que todo aparezca en una sola línea hemos usado print sin el salto de línea.

Java desde Cero – Estructuras de Control de Flujo – Condicionales

Para hacer referencia a las estructuras de control hay que entender como se ejecuta un programa, el como se ejecuta un programa es conocido como el flujo de ejecución, el cual básicamente es el orden de como se ejecuta nuestro programa, y ese orden por defecto es ir de arriba hacia abajo y secuencial una sentencia a otra; sin embargo, existen estructuras que pueden alterar ese flujo a través de saltos en función del cumplimiento de una condición, condiciones o la repetición de una instrucción o instrucciones una cantidad determinada o indeterminada de veces en función de una condición (estas dos últimas son conocidas como bucles), ahora bien el cumplimiento o no de la condición altera el flujo de ejecución del programa dadas las instrucciones que se implementen en función de cada condición.

Las estructuras de control de flujo de ejecución más básicas son los condicionales “Sí .. entonces” de la lógica proposicional del tipo condicional material que en programación se implementan con la palabra reservada “if“, podemos decir:

v_f_1

 

Es decir, si se cumple la condición se ejecuta y sino la sentencia que contiene no se ejecuta, tanto si se ejecuta o no existe un cambio en el flujo de ejecución, la estructura básica del condicional “if” es la siguiente:

if(condición) {
   sentencias
}

Los condicionales if pueden ser anidados, es decir que puede incluirse otro condicionales if dentro de un condicional if:

if(condicion1) {
      if(condicion2) {
            if(condición3) {
                       ...
            }
    }
}

La condición puede ser una o varias y se expresan a través de los operadores, por ejemplo podríamos decir que un usuario para acceder a una beca debe ser mayor de 18 años y menor que 40 años, ahora la concatenación (conjunción) de las condiciones se realiza con la palabra reservada “&&” por ejemplo sea x la edad implicará que ambas deban ser verdad, entonces:

if(x>=18 && x<=40) {
       System.out.println("Accede a la beca");
}

La disyunción, se expresa con la palabra reservada “||” e implicará que al menos una de las condiciones sea verdadera, por ejemplo sea x una variable que representa un día de la semana tal que si es viernes, sábado o domingo es considerado como el fin de semana:

if(x=="viernes" || x=="sábado" || x=="domingo") {
        System.out.println("Es fin de semana");
}

Un condicional “if” puede ir acompañado (es decir que no es obligatorio) de la instrucción “else” que es el sino, por ejemplo:

if(edad>=18) {
     System.out.println("Eres mayor de edad");
}else {
     System.out.println("No eres mayor edad");
}

Vamos a realizar un ejemplo en el que un usuario ingrese su edad y la edad se evalúe si es adolescente, joven, maduro o anciano en función de unos intervalos, recordemos que la entrada de datos lo hacemos a través de la clase Scanner que ya hemos tratado en un anterior post, entonces:

Scanner entrada=new Scanner(System.in);
System.out.println("Ingrese su edad");
int edad=entrada.nextInt();

if(edad<18) {
    System.out.println("Eres un adolescente");
}
if(edad>=18 && edad<40) {
   System.out.println("Eres Joven");
}
if(edad>=40 && edad<65) {
   System.out.println("Eres maduro");
}
if(edad>=65) {
   System.out.println("Eres un anciano");
}
entrada.close();

Sin embargo este tipo de estructura resulta un poco compleja ya que se debe definir las cotas tanto superior o inferior, además que aprovechamos el flujo de ejecución hasta que encaje dentro de alguna de las condiciones; sin embargo, podemos simplificar usando la palabra reservada “else if” de la siguiente forma:

 Scanner entrada= new Scanner(System.in);
if(edad<18) {System.out.println("Eres adolescente");}
else if (edad<40) {System.out.println("Eres joven");}
else if(edad<65) {System.out.println("Eres maduro");}
else {System.out.println("Eres un anciano");}
entrada.close();

Vemos que lo que ocurre es, si es menor que 18 entra en el primer condicional, si en caso de ser mayor que 18 evaluará la segunda condición que si es menor que 40 y si no evaluará si es menor que 65 y que implícitamente ya se comprobó que es mayor que 40 y así sucesivamente hasta quedar solo la última opción  “else“.

Bueno, esto ha sido lo más básico de los condicionales if que podemos tratar

 

 

Java desde Cero – Entrada y Salida de Datos – Formato de Resultados

Cuando queremos sacar información hacia la consola es común hacer uso de la instrucción:

System.out.println("Lo que queremos que salga");

Sin embargo, si deseamos ingresar datos a través de la consola hacia nuestra aplicación debemos de usar la clase Scanner el cual pertenece al paquete java.util. La clase Scanner posee tres métodos importantes: nextLine, nextInt, nextDouble (en la API puedes encontrar muchos más; sin embargo, podría decirse que estos son los más usados); otra alternativa sencilla es usar la clase JOptionPane y su método showInputDialog (este método es estático, lo cual básicamente indica que el nombre de la clase siempre debe ir por delante de la llamada al método y se verá más adelante). Si deseamos hacer uso de la clase Scanner debemos de importar el paquete útil; sin embargo, existen 2 formas en que podemos realizar esto, podemos importar todo el paquete dentro de los cuales se encuentra la clase Scanner o simplemente importar la clase, yo recomendaría que para un uso más eficiente se importe lo estrictamente necesario aun cuando en nuestros tiempos los espacios en memoria son lo suficientemente grandes, esto obedece más a una cuestión de eficiencia y buena práctica que a la funcionalidad ya que ambas formas funcionarán, entonces te dejo las dos formas:

import java.util.Scanner;
import java.util.*;

Hecha la importación debemos de crear una instancia de la clase, recordemos que java es un lenguaje orientado a objetos, entonces los objetos son tipos de datos referenciados lo que significa que si tenemos una clase el poder usarla requiere que se creen representaciones a las que llamamos instancias que hacen uso de las propiedades y métodos de dicha clase (sé que puede leerse un poco complicado pero a medida que avancemos podremos tener un mejor entendimiento del mismo); la clase Scanner tiene sobrecarga de constructores; es decir, que se puede crear una instancia de distintas formas en función de los parámetros que esta pueda requerir, dentro de los constructores nos interesa la forma en que podemos crear una instancia de clase que tenga como parámetro un flujo de entrada, y esto se obtiene con:

Scanner(InputStream source)

Un flujo de entrada lo obtenemos con la instrucción:

System.in

De la clase System del paquete lang (el paquete por defecto de java y que en consecuencia no requiere ser importado), el cual la entrada de flujo estándar que está abierta y lista para suministrar datos, típicamente este flujo corresponde a las entradas de teclado, entonces en nuestro código usamos:

Scanner entrada= new Scanner(System.in);

Podemos notar el uso de la palabra reservado new, la cual es parte de la sintaxis en la construcción de una instancia de una clase, es decir que siempre que hemos de crear una instancia de cualquier clase hemos de usar dicha palabra reservada.

Cuando tenemos ya la instancia creada vemos que es capaz de realizar un paro del flujo de ejecución a la espera del ingreso de un dato a través de la consola; sin embargo, el usuario no se dará por enterado sino es que se lo anunciamos,  por ejemplo:

Scanner entrada= new Scanner(System.in);
System.out.println("Ingrese un número");
int numero=entrada.nextInt();
System.out.println(numero);

Como puede observarse hemos creado la instancia de la clase y hemos impreso una solicitud que es el que el usuario ingrese un número, hasta que el usuario no presione la tecla ENTER la consola estará a la espera de la entrada de datos (a través del teclado), pero no solo basta con eso ya que por defecto nos retorna un token, debemos de hacer una conversión de ese token a un tipo de dato como una cadena, un entero o un flotante, es ahí que entran a tallar los métodos que hemos mencionado líneas arriba, en nuestro hemos convertido a un número entero a través del método nextInt (puede ser nextLine o nextDouble) lo hemos almacenado en una variable de tipo entero llamada “número”  y luego realizamos una impresión en consola; ahora veamos un ejemplo un poco más complejo:

public static void main(String[] args) {
     Scanner entrada= new Scanner(System.in);
     System.out.println("Ingresa tu nombre");
     String nombre=entrada.nextLine();
     System.out.println("Ahora ingresa tu edad por favor");
     int numero=entrada.nextInt();
     System.out.println("Tu nombre es: "+nombre+" y tu edad es de: "+numero + " años");
     entrada.close();
}  

Observamos que lo nuevo es el uso del método close, este método nos permite cerrar el flujo de entrada de datos, es decir cierra el Scanner, es propio y como buena práctica cerrar todo cuando ya hemos dejado de hacer uso de algún input, hecha esta observación debemos de obtener algo como esto:

e_s_1.png

Ahora bien las salidas pueden seguir diferentes tipos de formatos, imaginemos que tenemos una variable y queremos dividirla entre 3 e imprimir el resultado:

double x=1000.0;
System.out.println(x/3);
//resultado 333.3333333333333

Nosotros podemos dar un formato a la salida de datos, para ello debemos de usar el método printf de la clase PrintStream que se obtiene a través de la clase System y el campo out (al igual que in de entrada out de salida de flujo de datos), este método tiene sobrecarga, pero el que nos interesa es:

printf(String format,Object ... args);

Los tipos de formato los podemos obtener de Format string syntax, entonces por ejemplo si de nuestra división de x quisiéramos que solo se imprima a dos decimales debemos hacer lo siguiente:

System.out.printf("%1.2f", x/3); // resultado 333,33

Puedes experimentar con muchas de los formatos existentes, pero ello ya te lo dejo a tu criterio. Saludos.

Arduino Ethernet Shield y Python – parte 2

Anteriormente

PROGRAMACIÓN EN PYTHON

En esta ocasión abordaremos el caso desde python, para así completar todo lo necesario de nuestro proyecto, recordemos que queremos obtener la data desde arduino a través del protocolo udp haciendo uso del ethernet shield y  el arduino, para ello en la primera etapa hemos implementado el servidor que despacha los datos sobre la temperatura.

esquema_general2

Bien, ahora lo que debemos hacer es implementar desde el lado de python el recibir esa información y esto lo vamos a realizar a través del uso de los sockets, un socket es una comunicación bidireccional que se establece en los procesos y estos procesos pueden ser en una misma máquina o en diferentes, así también pueden desplegarse en diferentes protocolos siendo los más emblemáticos tcp/ip y udp. Python nos brinda un módulo para la implementación de sockets, este módulo es socket (muchas vueltas al asunto ¿no?).

Para ello creamos un fichero python con el nombre que quieras en nuestro IDE favorito ( yo sé que quieres decir que es VSCODE), hecho esto importamos el módulo socket y el módulo time con el fin de realizar pausas en el pedido de datos:

from socket import *
import time

Ahora debemos de crear una instancia de socket, esto lo podemos hacer con la función socket() el cual recibe dos parámetros, el primer parámetro hace referencia a la familia de direcciones y el segundo hace referencia al protocolo específico a usar, en nuestro caso hemos de usar IPv4 y UDP respectivamente:

cliente_socket=socket(AF_INET,SOCK_DGRAM)

En una tupla agregamos tanto el ip como el puerto por el cual nos comunicamos al servidor, recordemos que es: 192.168.1.48 y 3000 respectivamente:

direccion=("192.168.1.48",3000)

Agregamos un segundo de espera en alguna respuesta de parte del arduino a través del método settimeout():

cliente_socket.settimeout(1)

Creamos un bucle infinito de solicitudes al servidor, esto lo haremos a través de un while, dentro de el  debemos de enviar la solicitud, para ello definimos un string llamado data y le asignamos un valor de 1 (recuerda que puede ser el valor que se te antoje), luego de ello debemos enviarlo con el método sendto, este método recibe como parámetros la data de tipo byte y  la dirección que es una tupla, pero nuestra data es un string, entonces debemos de realizar una casteo a byte y eso se realiza con el método encode quedando de la siguiente forma:

cliente_socket.sendto(data.encode(),direccion)

Cuando hemos enviado la información deberíamos poder recibir algún tipo de dato, aunque esto no siempre se cumple, es por ello que debemos de hacer la lectura de los datos recibidos por el servidor a través de una estructura try/except, dentro del try/except  recibimos el dato a través del método recvfrom() que recibe como parámetro el tamaño del buffer, este método nos devuelve un par de elementos que son tanto la data en bytes y la dirección de donde provino, esencialmente estamos interesados en la data que es la temperatura (rec_data), recibido ello podemos hacer un casteo y pasarlo a float y finalmente la impresión del dato, quedando de la siguiente forma:

try:

   rec_data,addr=cliente_socket.recvfrom(2048) 

   temperatura=float(rec_data) 

   print("La temperatura es: {0}".format(temperatura))

except:

   print("Algo anda mal")

En el except hemos colocado información de que algo anda mal, podríamos sí capturar el error que se produce y hacerle un seguimiento; sin embargo, para fines del ejemplo no lo haremos, finalmente hacemos que nuestro, agregamos un descanso para la solicitud de 2 segundos:

time.sleep(2)
Y finalmente todo el código del fichero queda de la siguiente forma:
from socket import *

import time

direccion=("192.168.1.48",3000)

cliente_socket=socket(AF_INET,SOCK_DGRAM)

cliente_socket.settimeout(1)

while(1):

   data="1" #la data que enviaremos

   cliente_socket.sendto(data.encode(),direccion) 

   try:

      rec_data,addr=cliente_socket.recvfrom(2048) 

      temperatura=float(rec_data) 

      print("La temperatura es: {0}".format(temperatura))

   except:

      print("Algo anda mal")

   time.sleep(2)
Ahora aquí una imagen de la data recibida, recordar que la data recibida deben de traducirla a la medida en centígrados en la que se encuentren:

 

datafinal