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

Los Windows Runtime Components

Hace tiempo descubrí (en MVA) los Runtime Components, estos funcionan como librerías que nos permiten compartir código de distintos lenguajes dentro de una misma plataforma. Es decir, podemos usar código escrito en Visual Basic dentro de una aplicación hecha con C#, o un caso más extremo, y el que más me "impresionó": usar C++ en una aplicación hecha con Javascript. Y digo dentro de una misma plataforma ya que al contrario de las PCL, en este caso solo pueden funcionar para un solo sistema.

Lo que se genera junto con este tipo de proyectos son los metadatos para hacer nuestro código accesibles desde cualquier otro proyecto que haga referencia a él, de tal manera que si creamos un componente en C# desde C++ lo podamos llamar sin escribir ni una sola línea más de código y con la sintaxis del lenguaje desde el que estemos llamándolo.

En este caso me referiré a Windows 8, sin embargo me parece que también existen para Windows Phone 8. El ejemplo es bastante sencillo se trata de un método para realizar una suma, sin embargo es bastante extendible y muy funcional cuando se sabe como usarlos. Desde luego, tiene sus resticciones, pero siempre hay alguna forma de darles la vuelta o encontrar una mejor manera de hacerlo (¡por algo son restricciones!)

De tal manera que podríamos tener el siguiente código en C++:

Suma.h
#pragma once

namespace BlogPost_Component
{
public ref class Suma sealed
{
public:
// Propiedades
int a;
int b;
// Métodos
int Resultado();
// Constructores
Suma(int x, int y);

};
}

Suma.cpp
// Suma.cpp
#include "pch.h"
#include "Suma.h"

using namespace BlogPost_Component;
using namespace Platform;

Suma::Suma(int x, int y)
{
this->a = x;
this->b = y;
}

int Suma::Resultado(){
return a + b;
}

De tal modo que podemos usar la clase desde C# de esta manera:
private void Button_Click(object sender, RoutedEventArgs e)
{
int n1 = int.Parse(numero1.Text);
int n2 = int.Parse(numero2.Text);

var suma = new BlogPost_Component.Suma(n1,n2);

resultado.Text = suma.Resultado().ToString();
}

En JavaScript así:
function accionBoton(sender) {
var n1 = parseInt(document.getElementById('numero1').value);
var n2 = parseInt(document.getElementById('numero2').value);

var suma = new BlogPost_Component.Suma(n1, n2);

document.getElementById('resultado').textContent = suma.resultado();
};
Y en Visual Basic así:
Private Sub Button_Click(sender As Object, e As RoutedEventArgs)
Dim n1 As Integer = Integer.Parse(numero1.Text)
Dim n2 As Integer = Integer.Parse(numero2.Text)

Dim suma As New BlogPost_Component.Suma(n1, n2)

resultado.Text = suma.Resultado().ToString()
End Sub

El código del ejemplo anterior puede ser descargado aquí.

Como hobbie me puse a convertir el proyecto de la Codificación de Huffman a una aplicación para Windows 8 (escrita en JavaScript con el código de la codificación en C++), hasta el momento me he encontrado con algunas dificultades relacionadas con el acceso a archivos desde aplicaciones Metro, como sea, el proyecto está en GitHub.

PD: Lamento mencionarlo hasta ahora, pero solo es posible crear los componentes en C#, C++ y Visual Basic, lo sentimos, JavaScript.

¡Saludos!
@fferegrino :)

Programación dinámica y la sucesión de Fibonacci

(Wikipediazo)En informática, la programación dinámica es un método para reducir el tiempo de ejecución de un algoritmo mediante la utilización de subproblemas superpuestos y subestructuras óptimas.

Decir que un problema tiene subproblemas superpuestos es decir que se usa un mismo subproblema para resolver diferentes problemas mayores. Por ejemplo, en la sucesión de Fibonacci (F3 = F1 + F2 y F4 = F2 + F3) calcular cada término supone calcular F2. Como para calcular F5 hacen falta tanto F3 como F4, una mala implementación para calcular F5 acabará calculando F2 dos o más veces.

Esto se puede evitar guardando las soluciones que ya hemos calculado. Entonces, si necesitamos resolver el mismo problema más tarde, podemos obtener la solución de la lista de soluciones calculadas y reutilizarla. Este acercamiento al problema se llama memoización (no confundir con memorización; en inglés es llamado memoization, véase en). Si estamos seguros de que no volveremos a necesitar una solución en concreto, la podemos descartar para ahorrar espacio.

Comenzando con el pseudocódigo:
FUNC Fibonacci (↓n: NATURAL): NATURAL
VARIABLES
tabla: ARRAY [0..n] DE NATURALES
i: NATURAL
INICIO
SI n = 0 ENTONCES
DEVOLVER 0
SINOSI n = 1 ENTONCES
DEVOLVER 1
SINO
tabla[0] := 0
tabla[1] := 1
PARA i = 2 HASTA n HACER
tabla[i] := tabla[i-1] + tabla[i-2]
FINPARA
DEVOLVER tabla[n]
FINSI
FIN

Una implementación en C de este algoritmo es la siguiente (he cambiado los tipos de dato para que se puedan calcular valores más grandes):
long long fibonacciArreglo(int n) {
long long * lista;
if(n == 0) return 0;
if(n == 1) return 1;
lista = (long long *) malloc(sizeof(long long) * n);
lista[0] = 0;
lista[1] = 1;
int i;
for (i = 2; i <= n; i++) {
lista[i] = lista[i-1] + lista[i-2];
}
return lista[n];
}
Como podemos ver, se reservan n espacios de memoria para almacenar todos los valores de la sucesión, aunque solamente se emplean dos de estos valores a la hora de cálcular el siguente número. Lo cual deriva en un "desperdicio" de memoria. 

Es por eso que basándome en el enunciado que está más arriba en negritas, pensé que implementar una cola sería una opción para ahorrar espacio, podemos agregar dinámicamente los nuevos valores y quitar los que ya no usaremos, de tal manera solo tendríamos en memoria los valores necesarios para calcular el siguiente número en la sucesión. El código queda como sigue:
long long fibonacciCola(int n) {
Cola cola;
if (n == 0) return 0;
if (n == 1) return 1;
creaLista(&cola);
formar(&cola, 0);
formar(&cola, 1);
int i;
for (i = 2; i <= n; i++) {
// Quitamos el elemento menos reciente
long long fi1 = atender(&cola);
// Obtenemos el valor del siguiente más reciente
long long fi2 = valorPrincipio(&cola);
// Almacenamos el resultado en la cola
formar(&cola, fi1 + fi2);
}
return valorFinal(&cola);
}
De esta forma aseguramos que para cualquier tamaño de n solo se ocupen a lo más 3 espacios de memoria.

El código está disponible en la sección de extras del proyecto analizando-algo

¡Saludos!
@fferegrino :)

AndroidWebServer

La aplicación

Para construir un servidor web es necesario contar con un programa que haga las veces de “servidor”, es decir, que esté escuchando siempre en uno o varios sockets a la espera de algún cliente para servirle la información solicitada.
La interfaz gráfica será muy sencilla, una etiqueta para mostrar la IP del servidor, un botón para encender o apagar el servidor web y una consola para ver que es lo que está sucediendo, es decir llevar un registro de eventos.

Antes de continuar: Este trabajo está también disponible en versión PDF, lo cual considero, es más cómodo de leer. Lo puedes descargar desde aquí.

Interfaz gráfica

Como se menciona anteriormente, la interfaz es bastante simple, pensando en que de inicio el servidor no es una aplicación que tenga que ser manipulada constantemente por el usuario final.
La interfaz queda abierta a ampliaciones, según se vaya dotando de funcionalidad a esta aplicación.

ServerSocket

Sobre la plataforma Android, el manejo de los sockets es similar al que se realiza en cualquier otra aplicación de escritorio.
Así que simplemente abrimos un ServerSocket
//Fragmento de código del archivo WebServerActivity.java
ServerSocket ss;
try {
    ss = new ServerSocket(puerto);
    while (!isCancelled()) {
        Socket entrante = ss.accept();
//...

Permisos

Para hacer uso de los sockets es necesario otorgar permiso de INTERNET en el manifiesto de la aplicación, lo cual se consigue añadiendo la línea
<uses-permission android:name="android.permission.INTERNET" />
Ya que de otro modo  obtendremos una excepción de IO.

Complicaciones con el manejo de los sockets

En Android no es posible trabajar con sockets en el hilo principal, por tanto no es posible utilizar el código anteriormente mostrado dentro de cualquier método. Por ejemplo, no podemos colocarlo en el método onCreate, ni colocarlo directamente dentro de algún manejador de eventos ya que al tratar de ejecutarlo nos lanzará la excepción:
android.os.NetworkOnMainThreadException
Por tanto es necesario utilizar una clase interna que extienda de AsyncTask para trabajar con el ServerSocket
//Fragmento de código del archivo WebServerActivity.java
public class ServidorWeb
    extends AsyncTask<WebServerActivity, String, Void> {

En cuyo método sobreescrito doInBackground, añadiremos el código necesario para inicializar el socket:
//Fragmento de código del archivo WebServerActivity.java
protected Void doInBackground(WebServerActivity... arg0) {
    try {
        ss = new ServerSocket(puerto);
        while (!isCancelled()) {
            Socket entrante = ss.accept();

Para comenzar la ejecución basta con crear un objeto de nuestra clase nueva, ServidorWeb y llamar al método execute, por la manera en la que se trabajó, al método es necesario pasarle la referencia a la clase de nuestra actividad.
//Fragmento de código del archivo WebServerActivity.java
ServidorWeb servidorWeb;
servidorWeb = new ServidorWeb();
servidorWeb.execute(this);

Registro de eventos

En la interfaz gráfica incluimos un control (View) para llevar el registro de eventos, un símil de consola, pero sin serlo necesariamente, ya que no admite parámetros de entrada ni escritura sobre ella.
Para añadir texto bastaría con llamar al método setText, sin embargo es aquí en donde existe otra limitante de Android, y es que los controles solo pueden ser manipulados directamente por el hilo que los creó, todos los controles son creados por el hilo de UI, y no se pueden modificar por otro sino ese. Recordaremos que los eventos son lanzados desde el servidor, que está en otra clase, y peor aún, corre en otro hilo.
Hacer una llamada como: ServerConsole.append(Html.fromHtml("Buen día señor sol")); desde cualquier otra clase que no sea la de la actividad en cuestión resultará en una excepción:
android.view.ViewRoot$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.

Para nuestra suerte, existe una solución y es haciendo uso del método onProgressUpdate,con el cual podemos modificar cosas que estén corriendo en el hilo de UI
//Fragmento de código del archivo WebServerActivity.java
@Override
protected void onProgressUpdate(String... values) {
      /* logView es un método implementado en la clase WebServerActivity, en donde fueron creados los controles de la interfas. Se encarga de añadir el texto al control ServerConsole */
      logView((values[0]), level);
}
Contrario a lo que pareciera uno no debe llamar directamente a este método. Si no hacer uso del método (también proporcionado por la clase AsyncTask) publishProgress el cual, a cada llamada, provocará que el método onProgressUpdate se ejecute.
Esa es la manera en la que estaremos modificando la interfaz desde el hilo en el que correrá nuestro servidor.

Apagando y encendiendo el servidor

Para terminar la ejecución del código existente dentro de una AsyncTask tenemos a nuestra disposición el método cancel, del cual nos vamos a valer para apagar el servidor si es que no queremos que siga procesando solicitudes, dada la arquitectura de nuestro servidor, también es necesario cerrar el socket, ya que al estar esperando un cliente, no se apaga de inmediato.
//Fragmento de código del archivo WebServerActivity.java
servidorWeb.cancel(true);
servidorWeb.closeSocket();

Aceptando, leyendo y respondiendo la petición

Una vez que todo el esqueleto esté armado, es necesario adentrarnos a un nivel inferior, al núcleo del servidor, la forma en la que se hace una petición web es aceptando la conexión con un socket creado internamente, a través del cual podemos leer y escribir información al cliente que lo solicite.
Para realizar esto tenemos el siguiente código dentro de doInBackground:
//Fragmento de código del archivo WebServerActivity.java
///Mientras la tarea no haya sido cancelada
while (!isCancelled()) {
      ///Quedamos a la espera de un cliente nuevo
      Socket entrante = ss.accept();
///Actualizamos la “consola” con la información del cliente
///aceptado
      publishProgress("Cliente aceptado: <b>"                                     + entrante.getInetAddress().getHostName() + "</b>",
            "1");
///Usaremos la clase auxiliar PeticionWeb para procesar
      PeticionWeb pw = new PeticionWeb(entrante, this);
///Dado que PeticionWeb extiende de Thread, lo iniciamos
      pw.start();
}

La clase PeticionWeb


//Fragmento de código del archivo PeticionWeb.java
public class PeticionWeb extends Thread {
      private ServidorWeb web;
      privateSocket scliente = null;
Esta clase será el auxiliar encargado de procesar la petición, extiende de Threadpara que corra de manera que no bloquee a las demás peticiones entrantes. En ella se obtienen los flujos de entrada y de salida, que son los canales de comunicación con el cliente que está interesado en consumir de nuestro servidor.
El siguiente código es la implementación sobreescrita de run adaptada a nuestras necesidades.

//Fragmento de código del archivo PeticionWeb.java
@Override
public void run() {
      try {
            /* Obtenemos los flujos de entrada y salida, así como un objeto instancia de PrintWriter para escribir en el cliente */
            BufferedReader in = new BufferedReader(newInputStreamReader(
                        scliente.getInputStream()));
            OutputStream outputStream = scliente.getOutputStream();
            PrintWriter out = new PrintWriter(newOutputStreamWriter(
                        outputStream), true);
            out.flush();

            String cadena = "";
            /* ProcesaPeticion es la clase que usaremos para recuperar el contenido de los archivos almacenados en las carpetas del servidor */
            ProcesaPeticion pp = null;
            /* Leemos del flujo de entrada, mientras la lectura no sea nula continuaremos */
            while ((cadena = in.readLine()) != null) {

                  if (cadena != null ///es
&& !"".equals(cadena)) {
/* La lectura de una línea en blanco marca el final de la petición HTTP */
                        if (cadena.startsWith("GET")) {
                        /* Revisamos el contenido de la cadena que comience con GET, ya que es ahí en donde viene la información del archivo que se desea consultar */
                             cadena = cadena.substring(4);
                  cadena = cadena.substring(0,  
cadena.indexOf(" "));
                             web.log("Petición: " + cadena);
/* Dejamos que la clase auxiliar trate de buscar el archivo y devolver una respuesta */
                             pp = newProcesaPeticion(cadena);
                        }
                  } else
                        break;
            }
/* Escribimos las cabeceras de la respuesta y el contenido solicitado, el cual nos lo devolverá el método getResponse */
            out.println("HTTP/1.0 200 OK");
            out.println("Server: " + web.getIpAddr());
            out.println("Date: " + new Date());
            out.println("Content-Type: text/html");
            out.println("");
            if (pp != null) {
                  out.println(pp.getResponse());
            }
            out.flush();
            out.close();
      } catch (Exception e) {
            web.log("Error en servidor: " + e.toString(), "2");
      }
}

La clase ProcesaPeticion

Esta clase es el puente entre los archivos físicos en el servidor y la respuesta que el cliente espera recibir del servidor, no olvidar que, como estamos guardando los archivos del servidor en la memoria externa del dispositivo necesitamos añadir otro permiso al manifiesto de la app.
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
El código:
public class ProcesaPeticion {
      String path;
      String response;

      public ProcesaPeticion(String path) throwsException {
            this.path = path;
            // Checamos si está montada la SD:
            String state = Environment.getExternalStorageState();
            if (Environment.MEDIA_MOUNTED.equals(state)) {
                  // Variables File que nos ayudarán más adelante
                  File sdCard = Environment.getExternalStorageDirectory()
,peticion
,contenedor;
                  // Los documentos deberán existir en la carpeta
                  // "AndroidWebServer/wwwroot"
                  contenedor = new File(sdCard.getAbsolutePath()
                             + "/AndroidWebServer/wwwroot");
                  contenedor.mkdirs();
                  File request = new File(contenedor.getAbsolutePath() + path);
                  StringBuilder strLine = new StringBuilder();
                  if(request.exists() && request.isFile()) {
                        try {
                             // Lectura del archivo
                        } catch (Exception e) {
                        }
                  } else if(request.exists() && request.isDirectory()){
                        // Aquí manejaremos cuando la solicitud sea a un // directorio
                  } else {
                        // La ruta que solicitó no existe
                  }
                  response = strLine.toString();
            } else if (Environment.MEDIA_MOUNTED_READ_ONLY.equals(state)) {
            } else {
                  throw new Exception("No hay una memoria SD en el dispositivo");
            }
      }
     
}



Referencias

Wikipedia
Documentación de Android Developers



Contacto

Si tienes alguna duda con respecto a la aplicación puedes dejar un comentario en la siguiente liga
Enviarme un correo a antonio.feregrino@gmail.com
Mandarme un tweet a @fferegrino o @IO_Exception
No olvides que la aplicación está disponible en Google Play, la tienda de aplicaciones para Android en la siguiente liga:


¡Saludos!
@fferegrino :)