viernes, 25 de diciembre de 2015

Serie de Fibonacci

Alejandro tiene un campo y su vecina, Selena, tiene una pareja de conejos. Selena quiere asociarse con Alejandro para la cría de conejos, le dice que en su campo pueden criarse juntos y multiplicarse. Le dice que al mes engendrarán una pareja de conejos y que esa pereja tardará un mes en engendrar otra pareja de conejos. A su vez la primer pareja seguirá engendrando parejas de conejos. Y así cada pareja de conejos seguirá teniendo parejas de conejos. Entonces Alejandro se pregunta: ¿Cuántos conejos tendrá al cabo de 12 meses? La respuesta está en la serie de Fibonacci.

Los números de Fibonacci Fk son una sucesión de números naturales definidos de la siguiente manera:
F0 = 0,
F1 = 1,
Fk = Fk−1 + Fk−2, cuando k≥2

En palabras simples, la sucesión de Fibonacci comienza con 0 y 1, y los siguientes términos siempre son la suma de los dos anteriores.



En la siguiente tabla, podemos ver los números de Fibonacci desde el 0-ésimo hasta el duodécimo.

n 0123456789101112...
Fn 01123581321345589144...


La sucesión puede utilizarse, de forma parecida, para contar el número de distintas rutas que puede seguir una abeja que va recorriendo las celdillas hexagonales de un panal.

Objetivo

Dado un número N obtener el número de Fibonacci n-ésimo.

La solución recursiva en Perl


La serie de Fibonacci puede definirse en forma recursiva, y es natural pensar una solución recursiva como eśta:

#!/usr/bin/perl
use strict;
use warnings;

print "Serie Fibonacci\n";
print "Por favor, ingrese n: ";
my $n = <stdin>;
chomp($n);
my $valor= fibonacci($n);
print "El valor F($n) es $valor \n";

sub fibonacci{
  my $n= shift;
  # F0 = 0,F1 = 1,
  if ($n<=1){ return $n; }
  return fibonacci($n-1) + fibonacci($n-2);
}

Pero si estudiamos lo que pasa con éstas llamadas recursivas vemos que se llaman múltiples veces con los mismos argumentos. Esto es ineficiente, además las múltiples llamadas son exponenciales, por ejemplo fibonacci(5) llama a fibonacci(4) y fibonacci(3); pero fibonacci(4) también llama a fibonacci(3) y a fibonacci(2); pero cada fibonacci(3) llama a fibonacci(2) y así hasta llegar al caso base.

La solución con memoización en Perl

La solución más obvia es ir guardando los resultados parciales en un array para poder utilizarlos luego. Cada resultado parcial es la respuesta a un subproblema del problema inicial.

#!/usr/bin/perl
use strict;
use warnings;

print "Serie Fibonacci\n";
print "Por favor, ingrese n: ";
my $n = <stdin>;
chomp($n);
my @serie;
$serie[0]= 0;
$serie[1]= 1;
my $valor= fibonacci($n);
print "El valor F($n) es $valor \n";

sub fibonacci{
  my $n= shift;
  if (defined($serie[$n])){ return $serie[$n]; }
  $serie[$n]= fibonacci($n-1) + fibonacci($n-2);
  return $serie[$n];
}


Uno podría pensar que la memoización es la manera más conveniente de resolver un problema de recursividad múltiple. Sin embargo, una función recursiva gasta un poco de memoria y tiempo en cada llamada recursiva. Por esta razón, la programación dinámica prescinde de la recursividad y resuelve el problema exclusivamente con la iteración.

La solución con programación dinámica en Perl

En general los pasos necesarios para resolver un problema con la programación dinámica son las siguientes:
  • Encontrar una definición recursiva para el problema (incluyendo los casos base)
  • Inicializar la estructura de memoria (normalmente un vector o una matriz)
  • Llenar el resultado para los casos base
  • Usar bucles para calcular el resultado para otros casos, empleando la definición recursiva (estrategia bottom-up)
La estrategia bottom-up consiste en resolver primero los subproblemas más pequeños, almacenar su solución, y luego resolver los problemas más complejos, usando los resultados almacenados.


La idea es llenar el vector o matriz de resultados de abajo arriba, empezando por los casos base. Al resolver un subproblema se aplica la misma idea recursiva para obtener el resultado. Sin embargo, si los subproblemas recursivos ya han sido resueltos no es necesario hacer ninguna llamada recursiva, sino que el resultado se puede calcular directamente a partir de los resultados guardados en memoria.

#!/usr/bin/perl
use strict;
use warnings;

print "Serie Fibonacci\n";
print "Por favor, ingrese n: ";
my $n = <stdin>;
chomp($n);
my $valor= fibonacci($n);
print "El valor F($n) es $valor \n";

sub fibonacci{
  my $n= shift;
  my @serie;
  $serie[0]= 0;
  $serie[1]= 1;
  for (my $i= 2; $i <= $n; $i++){
    $serie[$i] = $serie[$i-2] + $serie[$i-1];
  }
  return $serie[$n];
}



viernes, 18 de diciembre de 2015

Números primos

Primo o compuesto


Luis invitó a su primo Miguel a su casa. Mientras la mamá de Luis preparaba unas galletas caseras al horno para comer con la leche chocolatada, los primos revolvían unos libros de matemática y se encontraron con este teorema:

Para todo número primo p > 3, se tiene que p = 6k+1 ó p = 6k-1 

Luego de pensar un rato se dieron cuenta de que todos los números enteros pueden expresarse exactamente de un de las 6 posibles formas:
6k, 6k+1, 6k+2, 6k+3, 6k-2, ó 6k-1
-Claro! -dijo Luis- 6k es divisible por 6, por lo que no es primo.
-6k+2 es par, por lo que no es primo -agregó Miguel.
-6k-2 es par, así que tampoco es primo -se apuró a conjeturar Luis.
-Pero qué pasa con 6k+3?
-6k+3 es igual a 3(2k+1) que es divisible por 3, por lo que no es primo
-Por lo tanto, los números primos tienen que expresarse de la forma 6k+1 o 6k-1.
-Pero no todos los números de esa forma son primos, por ejemplo...
-Ya están las galletitas! A tomar la leche! -dijo la mamá.



Objetivo

Desarrolle un programa cuya entrada sea un entero positivo n, y cuya salida sea:

    primo, si el número es primo, y
    compuesto, si el número es compuesto.

Por ejemplo, si la entrada es 29, el programa debe decir primo. Si la entrada es 27, el programa debe decir compuesto.


Solución en Perl


#!/usr/bin/perl
use strict;
use warnings;

print "Por favor, ingrese un número mayor a 0: ";
my $n = <stdin>;
chomp($n);
if (primo($n)){ print "Primo!"; }
else{ print "Compuesto!"; }
print "\n";

sub primo{
  my $n= shift;
  if (1==$n){ return 0; }
  if (2==$n){ return 1; }
  if (3==$n){ return 1; }
  return teorema($n);
}
sub teorema{
  my $n= shift;
  my $resto= $n % 6;
  if (($resto != 1) && ($resto != 5)){ return 0; }
  my $raiz= sqrt($n);
  my $i= 1;
  while ((6*$i - 1) <= $raiz){
    if ( !($n % (6*$i + 1)) ){ return 0; }
    if ( !($n % (6*$i - 1)) ){ return 0; }
    $i++;
  }
  return 1;
}

Primeros m primos

Usando como base el programa diseñado en el ejercicio anterior, desarrolle otro programa que reciba como entrada un número entero positivo m y cuya salida sean los m primeros números primos.

Por ejemplo, si la entrada es 12, la salida del programa debe ser:

2
3
5
7
11
13
17
19
23
29
31
37

Solución en Perl


#!/usr/bin/perl
use strict;
use warnings;

print "Por favor, ingrese un número mayor a 0: ";
my $n = <stdin>;
chomp($n);
my $cantidad= 0;
my $i= 1;
while ($cantidad < $n){
  if (primo($i)){ print "$i\n"; $cantidad++; }
  $i++;
}
print "\n";

sub primo{
  my $n= shift;
  if (1==$n){ return 0; }
  if (2==$n){ return 1; }
  if (3==$n){ return 1; }
  return teorema($n);
}
sub teorema{
  my $n= shift;
  my $resto= $n % 6;
  if (($resto != 1) && ($resto != 5)){ return 0; }
  my $raiz= sqrt($n);
  my $i= 1;
  while ((6*$i - 1) <= $raiz){
    if ( !($n % (6*$i + 1)) ){ return 0; }
    if ( !($n % (6*$i - 1)) ){ return 0; }
    $i++;
  }
  return 1;
}

Primos hasta m


Modifique el programa del ejercicio anterior para que muestre los números primos menores o iguales a m.

Por ejemplo, si la entrada es 12, la salida debe ser:

2
3
5
7
11

Solución en Perl


#!/usr/bin/perl
use strict;
use warnings;

print "Por favor, ingrese un número mayor a 0: ";
my $n = <stdin>;
chomp($n);
my $i= 1;
while ($i <= $n){
  if (primo($i)){ print "$i\n"; }
  $i++;
}
print "\n";

sub primo{
  my $n= shift;
  if (1==$n){ return 0; }
  if (2==$n){ return 1; }
  if (3==$n){ return 1; }
  return teorema($n);
}
sub teorema{
  my $n= shift;
  my $resto= $n % 6;
  if (($resto != 1) && ($resto != 5)){ return 0; }
  my $raiz= sqrt($n);
  my $i= 1;
  while ((6*$i - 1) <= $raiz){
    if ( !($n % (6*$i + 1)) ){ return 0; }
    if ( !($n % (6*$i - 1)) ){ return 0; }
    $i++;
  }
  return 1;
}

 Más información

Un ejemplo de algoritmo eficiente y su explicación se puede encontrar en la Criba de Atkin

miércoles, 16 de diciembre de 2015

PI

Un poco de historia

El valor de π se ha obtenido con diversas aproximaciones a lo largo de la historia, siendo una de las constantes matemáticas que más aparece en las ecuaciones de la física, junto con el número e. Por ello, tal vez sea la constante que más pasiones desata entre los matemáticos profesionales y aficionados.
Tomando en cuenta que el número pi forma un decimal infinito, lo habitual es usar una aproximación del mismo.

Los matemáticos han encontrado varias series matemáticas que si se repiten infinitamente pueden calcular con precisión el valor de Pi con una gran cantidad de decimales. Algunas de estas series son tan complejas que se necesitan supercomputadoras para procesarlas. Sin embargo, una de las más simples, es la serie Gregory-Leibniz. Aunque no es muy eficiente, se acerca cada vez más al valor de Pi en cada repetición, produciendo con precisión hasta cinco mil decimales de Pi con 500000 repeticiones.

Objetivo I

Desarolle un programa para estimar el valor de π usando la siguiente suma infinita:
π = 4*(1 − 1/3 + 1/5 − 1/7 + ···)

La entrada del programa debe ser un número entero n que indique cuántos términos de la suma se utilizará.

Por ejemplo, si la entrada es 3, el programa debe entregar como salida:

3.466666666666667

Si la entrada es 1000, la salida debe ser:

3.140592653839794

Mientras más veces repitas la serie, más te acercarás al valor de Pi.


Solución en Perl


#!/usr/bin/perl
use strict;
use warnings;

my $suma= 0;
print "Serie Gregory-Leibniz\n";
print "Por favor, ingrese la cantidad de terminos: ";
my $n = <stdin>;
chomp($n);
my $fin_iteracion= $n-1;
foreach my $i (0..$fin_iteracion){
  my $sumando= 1/($i*2+1);
  if ($i%2){ $suma-= $sumando; }
  else{ $suma+= $sumando; }
}
my $aproximacion= 4 * $suma;
print "El valor aproximado de Pi es $aproximacion \n";



Objetivo II

Utilice la serie Nilakantha. Esta es otra serie infinita que sirve para calcular Pi y que además es bastante fácil de entender. Aunque es más complicada que la fórmula de Gregory-Leibniz, converge en los valores de Pi mucho más rápido.

π = 3 + 4/(2*3*4) - 4/(4*5*6) + 4/(6*7*8) - 4/(8*9*10) + 4/(10*11*12) - 4/(12*13*14) ...
Para esta fórmula, toma un tres y empieza a alternar entre suma y resta de fracciones con un numerador de 4 y un denominador que sea el producto de tres enteros consecutivos que vayan aumentando con cada nueva fracción. El denominador de cada nueva fracción empieza con el mayor entero utilizado en la fracción anterior. Repite la serie aunque sea solo un par de veces y verás que el resultado se acerca bastante a Pi.



Solución en Perl


#!/usr/bin/perl
use strict;
use warnings;

my $suma= 0;
print "Serie Nilakantha\n";
print "Por favor, ingrese la cantidad de terminos: ";
my $n = <stdin>;
chomp($n);
foreach my $i (1..$n){
  my $denominador= ($i*2)*($i*2+1)*($i*2+2);
  my $sumando= 4/$denominador;
  if ($i%2){ $suma+= $sumando; }
  else{ $suma-= $sumando; }
}
my $aproximacion= 3 + $suma;
print "El valor aproximado de Pi es $aproximacion \n";

lunes, 14 de diciembre de 2015

Paradoja de la dicotomía

Problema

Zenón está a ocho metros de un árbol. Llegado un momento, lanza una piedra, tratando de dar al árbol. La piedra, para llegar al objetivo, tiene que recorrer antes la primera mitad de la distancia que le separa de él, es decir, los primeros cuatro metros, y tardará un tiempo (finito) en hacerlo. Una vez llegue a estar a cuatro metros del árbol, deberá recorrer los cuatro metros que le quedan, y para ello debe recorrer primero la mitad de esa distancia. Pero cuando esté a dos metros del árbol, tardará tiempo en recorrer el primer metro, y luego el primer medio metro restante, y luego el primer cuarto de metro... De este modo, la piedra nunca llegará al árbol. Es posible utilizar este razonamiento, de forma análoga, para «demostrar» que la piedra nunca llegará a salir de la mano de Zenón.

Potencias fraccionales de 2


Desarrolle un programa que tabule las potencias fraccionales de 2 (1/2, 1/4, 1/8, 1/16, ...) y sus sumas parciales en forma decimal.

La salida del programa debe comenzar así:

Potencia  Fraccion  Suma
1          0.5       0.5
2          0.25      0.75
3          0.125     0.875
4          0.0625    0.9375
...       ...

El programa debe terminar cuando la fracción sea menor o igual que 0.00001.

La tercera columna contiene la suma de todas las fracciones calculadas hasta esa fila.

Solución en Perl

Usamos printf para imprimir las fracciones con una cantidad específica de decimales por un motivo estético

#!/usr/bin/perl
use strict;
use warnings;

my $n= 1;
my $fraccion= 1/(2**$n);
my $suma= $fraccion;
print "Potencia \t\tFraccion \t\tSuma\n";
while ($fraccion>0.00001){
  print "$n \t\t";
  printf "%.16f \t\t%.5f \n",$fraccion,$suma;
  $n++;
  $fraccion= 1/(2**$n);
  $suma+= $fraccion;
}

martes, 8 de diciembre de 2015

Largo y corto

100 Nombres

Daniel y Cristina van a tener un bebé. Todavía no saben si será nene o nena pero tienen una lista con los posibles nombres. Como les cuesta decidirse por alguno quieren divertirse diseñando un programa de computadora que les diga el nombre más corto y el más largo.

Objetivo

Desarrolle un programa que tenga la siguiente entrada:
  • primero, el usuario ingresa un número entero n, que indica cuántas palabras ingresará a continuación;
  • después el usuario ingresa n palabras.

La salida del programa deben ser la palabra más larga y la más corta que ingresó el usuario.

Por ejemplo, si el usuario ingresa:

5
Diego
Andrea
Marcelino
Azul
Ana

la salida debe ser:

Mas larga: Marcelino
Mas corta: Ana

Solución en Perl


#!/usr/bin/perl
use strict;
use warnings;

my $mas_corto= '';
my $mas_largo= '';
my $size_corto= 99;
my $size_largo= 0;
print "Por favor, ingrese la cantidad de nombres: ";
my $n = <stdin>;
chomp($n);
foreach my $i (1..$n){
  print "Nombre $i?: ";
  my $nombre = <stdin>;
  chomp($nombre);
  if (length($nombre) > $size_largo){ $mas_largo= $nombre; $size_largo= length($nombre); }
  if (length($nombre) < $size_corto){ $mas_corto= $nombre; $size_corto= length($nombre); }
}
print "Gracias por los valores ingresados \n";
print "El más corto: $mas_corto \n";
print "El más largo: $mas_largo \n";


Contador de signos

Signos del clima

Ramón está preocupado por el cambio climático y decide empezar a revisar cifras que tengan que ver con el clima y su evolución. Para empezar toma un listado de temperaturas alrededor del mundo y quiere contar las que están sobre cero y las que están bajo cero. Como el trabajo es muy repetitivo y propenso a errores se propone automatizarlo con un programa de computadora.

Objetivo

Desarrolle un programa que pida al usuario que ingrese varios números, uno por uno. El ingreso de números termina cuando el usuario ingresa el 0, entonces el programa debe entregar como salida la cantidad de números positivos y negativos que ingresó el usuario.

Por ejemplo, si el usuario ingresa los números 37, —4, —22, —7, 17, —1 y 0, el programa debe entregar la salida:

Positivos: 2
Negativos: 4


Solución en Perl

#!/usr/bin/perl
use strict;
use warnings;

my $positivos= 0;
my $negativos= 0;
print "Por favor, ingrese un valor: ";
my $temperatura = <stdin>;
chomp($temperatura);
while($temperatura){
  if ($temperatura > 0){ $positivos++; }
  if ($temperatura < 0){ $negativos++; }
  print "Por favor, ingrese un valor: ";
  $temperatura = <stdin>;
  chomp($temperatura);
}
print "Gracias por los valores ingresados \n";
print "Positivos: $positivos \n";
print "Negativos: $negativos \n";

lunes, 30 de noviembre de 2015

Tablas de multiplicar

Pamela aprende a multiplicar

Pamela llega de la escuela contenta porque aprendió que la multiplicación le ahorra tiempo cuando tiene que sumar varias veces el mismo número. Se lo explica a su hermano mayor, David, y le pregunta dónde puede encontrar las tablas de multiplicar. David lo piensa dos veces y se le ocurre que es una buena oportunidad para llamar a su primo Vladimir. Vladimir llega con su netbook y su lenguaje de programación favorito y juntos planean cómo imprimir las tablas de multiplicar.

Objetivo

Desarrolle un programa que imprima la tabla de multiplicar del número elegido.
Por ejemplo, si se elige el número 9, la salida del programa debe ser:

9 x 1 = 9
9 x 2 = 18
9 x 3 = 27
9 x 4 = 36
9 x 5 = 45
9 x 6 = 54
9 x 7 = 63
9 x 8 = 72
9 x 9 = 81
9 x 10 = 90

Solución en Perl


#!/usr/bin/perl
use strict;
use warnings;

print "Ingresar el factor: ";
my $factor = <stdin>;
chomp($factor);
if (! $factor){ die; }

foreach my $i (1..10){
  my $producto= $i*$factor;
  print "$factor x $i = $producto \n";
}

lunes, 23 de noviembre de 2015

Resolver una ecuación cuadrática

Ecuación cuadrática


Desarrolle un programa que resuelva la ecuación cuadrática ax2+bx+c=0.

La entrada del programa serán a, b y c. La salida serán las soluciones reales de la ecuación.

Por ejemplo, si la entrada son los valores 1, 2 y —8, la salida del programa debe ser:

-4.0
2.0

Si la entrada son los valores 4, 4 y 1, la salida del programa debe ser:

-0.5

Si la entrada son los valores 3, —3 y 1, la salida del programa debe ser:

no hay soluciones reales

Solución


Resolveremos la ecuación reemplazando a, b y c en la fórmula cuadrática:
x= (-b ± √(b2- 4ac) )/ 2a

El discriminante de la fórmula determina cuántas soluciones reales hay, es decir la cantidad de valores de x que satisfacen la ecuación. Si el discriminante es cero hay una solución, si es mayor a 0 hay dos soluciones y si es negativo no hay soluciones reales. El discriminante se calcula como Δ= b2 - 4ac

Cuando el discriminante es cero, la fórmula cuadrática  que queda es x= -b/2a
Cuando el discriminante es mayor a cero nos quedan dos soluciones:
x= (-b + √Δ) / 2a
x= (-b - √Δ) / 2a

Solución en Javascript


function cuadratica(){
  var a;
  var b;
  var c;

  a= prompt( 'Ingresar el valor a: ', 1 );
  // si a es cero o vacio no es una función cuadrática
  if (! (a*1)) return;

  b= prompt( 'Ingresar el valor b: ', 1 );
  c= prompt( 'Ingresar el valor c: ', 1 );

  var discriminante= b*b - 4*a*c;
  if (discriminante < 0){ 
    alert( 'No hay soluciones reales' );
    return;
  }

  if (discriminante){ 
    alert( 'Hay dos soluciones:' ); 
    var solucion = (-b + Math.sqrt(discriminante))/(2*a);
    alert( solucion ); 
    var solucion2 = (-b - Math.sqrt(discriminante))/(2*a);
    alert( solucion2 ); 
  }else{
    var solucion = -b/(2*a);
    alert( 'La unica solucion es ' + solucion );
  }
  return;
}
cuadratica();
cuadratica();
cuadratica();

Solución en Perl


#!/usr/bin/perl
use strict;
use warnings;

print "Ingresar el valor a: ";
my $a = <stdin>;
chomp($a);
if (! $a){ die; }

print "Ingresar el valor b: ";
my $b = <stdin>;
chomp($b);

print "Ingresar el valor c: ";
my $c = <stdin>;
chomp($c);

my $discriminante= $b*$b - 4*$a*$c;
if ($discriminante < 0){ 
  print "No hay soluciones reales \n";
  exit;
}

if ($discriminante){ 
  print "Hay dos soluciones: \n"; 
  my $solucion = (-$b + sqrt($discriminante))/(2*$a);
  print $solucion."\n"; 
  my $solucion2 = (-$b - sqrt($discriminante))/(2*$a);
  print $solucion2."\n"; 
}else{
  my $solucion = -$b/(2*$a);
  print "La unica solucion es $solucion \n";
}


jueves, 19 de noviembre de 2015

Número recíproco


Recíproco o Inverso multiplicativo


El recíproco de un número real x se calcula como 1 dividido por x, excepto cuando x=0, ya que el cero no tiene recíproco.

Desarolle un programa que reciba como entrada un número real, y que entregue como salida el recíproco del número. Si la entrada es 0, la salida debe decir: el cero no tiene reciproco.

Solución en Perl

#!/usr/bin/perl
use strict;
use warnings;

print "Ingresar número: ";
my $valor = <stdin>;
chomp($valor);

if (! $valor){ print "El cero no tiene reciproco \n"; }
else{
  my $reciproco = 1/$valor;
  print "El reciproco de $valor es $reciproco \n";
}

Solución en Javascript

var valor= prompt('Ingresar número',0);
if (! valor){ alert('El cero no tiene reciproco'); }
else{
  var reciproco;
  reciproco = 1/valor;
  alert ('El reciproco de ' + valor + ' es ' + reciproco);
}


martes, 17 de noviembre de 2015

Planètes

Calculer la superficie et le volume d'une sphère


Le volume, en sciences physiques ou mathématiques, est une grandeur qui mesure l'extension d'un objet ou d'une partie de l'espace. La superficie d'un espace au sol ou d'une surface physique plane ou gauche est sa mesure physique exprimée avec une unité de mesure. L'unité correspondante du système international est le mètre carré1 ou l'un de ses multiples ou sous-multiples.
Dans cet exercice, nous proposons de développer un programme dont l'entrée est le rayon d'une planète et sa sortie:
  • La superficie de la planète
  • Le volumen de la planète
Les formules qui sont nécessaires:
Le volume d'une sphère est exprimé en termes de son rayon r\, comme:
V = \frac{4 \pi r^3}{3}
La superficie est 4 fois \pi \, fois son rayon au carré:
\ A = 4\pi r^2

Par exemple, si l'entrée est 6351 (le rayon moyen de la Terre en kilomètres), le programme doit fournir les résultats:
  • Superficie: 510064471.90978825
  • Volume: 1083206916845.7535

Perl Solution

#!/usr/bin/perl
use strict;
use warnings;
print "Entrez la rayon: ";
my $rayon = <stdin>;
chomp($rayon);

my $PI = 4.0 * atan2(1.0,1.0);

my $volume;
$volume = ($rayon * $rayon * $rayon * $PI * 4) / 3;
print "Le volume de la planète est $volume\n";

my $superficie;
$superficie = $rayon * $rayon * $PI * 4;
print "La superficie de la planète est $superficie\n";


Javascript Solution

var rayon= prompt('Entrez la rayon',0);
var volume;
volume = (rayon * rayon * rayon * Math.PI * 4) / 3;
alert ('Le volume de la planète est  ' + volume);
var superficie;
superficie = rayon * rayon * Math.PI * 4;
alert ('La superficie de la planète est ' + superficie);

sábado, 7 de noviembre de 2015

Conversión de temperaturas

Convertir de Grados Celsius a Fahrenheit


El grado Celsius o centígrado es la unidad termométrica que Anders Celsius definió en 1742 considerando las temperaturas de ebullición y de congelación del agua. Es muy utilizada para expresar las temperaturas de uso cotidiano. El grado Fahrenheit es una escala de temperatura propuesta por Daniel Fahrenheit en 1724 y se usa en la mayoría de los países anglosajones, aunque está siendo desplazado paulatinamente.
En este ejercicio proponemos desarrollar un programa cuya entrada sea una temperatura en grados Fahrenheit y su salida sea la temperatura en grados Celsius.
La fórmula de conversión es:
C= (F - 32) * 5/9
Por ejemplo, si la entrada es 131, el programa debe entregar el resultado 55.

Solución en Perl

#!/usr/bin/perl
use strict;
use warnings;
print "Ingrese la temperatura: ";
my $fah = <>;
chomp($fah);
my $cel;
$cel = ($fah - 32) * 5/9;
print "Temperatura de $fah grados Fahrenheit es $cel grados Celsius";

Solución en Javascript

var fah= prompt('Ingrese la temperatura',0);
var cel;
cel = (fah - 32) * 5/9;
alert ('Temperatura de ' + fah + ' grados Fahrenheit es ' + cel + ' grados Celsius');

domingo, 6 de septiembre de 2015

Convertir caracteres entre mayúsculas y minúsculas 2

Convertir el primer carácter de cada palabra del string



La función ucwords() de Php convierte el primer carácter de cada palabra a mayúscula.

// Ejemplo
<?
$foo= 'Viens, fait le pour moi!';
$bar= ucwords($foo); // Viens, Fait Le Pour Moi!
print $bar."\n";

Para hacer lo contrario, es decir, convertir a minúscula el primer carácter de cada palabra, tenemos que trabajar un poco más:


<?
/* 
    # lcwords v1.000
    # Convertir el primer carácter de cada palabra a minúscula
    # input string
    # return string
*/
function lcwords($string)
{
/* Variables temporales */
    # variable loop
    $a = 0;
    # almacenar todas las palabras en un array para luego unirlas en un string y retornarlo
    $string_new = array();
    $string_exp = explode(" ",$string);
    foreach($string_exp as $astring)
    {
        for($a=0;$a<strlen($astring);$a++)
        { 
            # chequear que el carácter es una letra
            # ejemplo: si la palabra es "100PRE" debería convertir la letra P solamente
            # chequear el carácter en la posicion $a
            if(preg_match("'\w'",$astring[$a]))
            {
                $astring[$a] = strtolower($astring[$a]);
                # conseguido! finalizar el bucle
                break;
            }
        }
        $string_new[] = $astring;
    }
    # devolver el string como la unión de las palabras separadas por un espacio
    return implode(" ",$string_new);
}

// Ejemplo 2
$foo = 'Viens, embrasse MOI!';
$bar = lcwords($foo); // viens, embrasse mOI!



En Javascript no hay una función ucwords() como en Php para convertir en mayúsculas la primera letra de cada palabra en un string, pero podemos crear una:

function ucwords(str){
return str.replace(/(^([a-zA-Z\p{M}]))|([ -][a-zA-Z\p{M}])/g,
        function($1){
            return $1.toUpperCase();
        });
}
//Ejemplo
str= "Tout ce qui nous a unis";
console.log(ucwords(str)); // Tout Ce Qui Nous A Unis

Para convertir a minúsculas la primera letra de cada palabra en un string en Javascript, podemos armar esta función lcwords:

function lcwords(str){
return str.replace(/(^([a-zA-Z\p{M}]))|([ -][a-zA-Z\p{M}])/g,
        function($1){
            return $1.toLowerCase();
        });
}
//Ejemplo
str= "Tout ce Qui NOUS a Unis";
console.log(lcwords(str)); // tout ce qui nOUS a unis

Para convertir el primer carácter de cada palabra de un string a mayúsculas o minúsculas en Perl tampoco existe una función ucwords() o lcwords(). Pero podemos usar split, map y join en conjunto para lograr ese efecto. Con split separamos el string en palabras, con map aplicamos una función a cada palabra, en este caso ucfirst o lcfirst, y luego con join volvemos a juntar el string. Veamos el ejemplo:

# Ejemplo 1
$texto = "CES TRaces quE Tu as laisse à Paris";
$texto = join '', map { ucfirst } split /(\s+)/, $texto;
# CES TRaces QuE Tu As Laisse à Paris
print "Texto: " . $texto;
# Ejemplo 2
$texto = "CES TRaces quE Tu as laisse à Paris";
$texto = join '', map { lcfirst } split /(\s+)/, $texto;
# cES tRaces quE tu as laisse à paris
print "Texto: " . $texto;

Un tercer ejemplo

Si además de pasar la primera letra a mayúsculas, queremos las demás en minúsculas, prueba agregar otra función en map, en este orden: ucfirst lc

# Ejemplo 3
$texto = "CES TRaces quE Tu as laisse à Paris";
$texto = join '', map { ucfirst lc } split /(\s+)/, $texto;
# Ces Traces Que Tu As Laisse à Paris
print "Texto: " . $texto;


martes, 1 de septiembre de 2015

Convertir caracteres entre mayúsculas y minúsculas


Convertir todos los caracteres del string


La función de Perl lc() regresa el string pasado como parámetro con todos sus caracteres en minúsculas. En cambio la función uc() regresa el string pasado como parámetro a mayúsculas.

my $s = "La comunidad de compra y venta OnLine de Europa\n";
print("Todo en mayúsculas:\n");
print(uc($s),"\n");
print("Todo en minúsculas:\n");
print(lc($s),"\n");

La función de Javascript toLowerCase() regresa el string pasado como parámetro con todos sus caracteres en minúsculas. En cambio la función toUpperCase() regresa el string pasado como parámetro a mayúsculas.

// Ejemplo 1
var str = "Hola Mundo!";
var res = str.toLowerCase(); 
// Ejemplo 2
var str = "Hola Mundo!";
var res = str.tUpperCase(); 

La función de Php strtolower() regresa el string pasado como parámetro con todos sus caracteres en minúsculas. En cambio la función strtoupper() regresa el string pasado como parámetro a mayúsculas.

$str = "SiguE a tus FavoritoS dESDe aQuí";
$str = strtoupper($str);
// muestra:  SIGUE A TUS FAVORITOS DESDE AQUÍ
echo $str; 
$str = "SiguE a tus FavoritoS dESDe aQuí";
$str = strtolower($str);
// muestra  sigue a tus favoritos desde aquí
echo $str; 

 

Convertir el primer carácter de un string


La función lcfirst() de Perl devuelve el string pasado como parámetro pero con la primera letra cambiada a minúsculas. La función ucfirst() de Perl devuelve el mismo string pero con la primera letra cambiada a mayúsculas.

print lcfirst $str;  // Muestra la primera letra en minúsculas
print ucfirst $str;  // Muestra la primera letra en mayúsculas

La función lcfirst() de Php devuelve el string pasado como parámetro pero con la primera letra cambiada a minúsculas. La función ucfirst() de Php devuelve el mismo string pero con la primera letra cambiada a mayúsculas.

$bar = 'HELLO WORLD!';
$bar = lcfirst($bar);             // hELLO WORLD!
$bar = ucfirst($bar);             // HELLO WORLD!

En Javascript no existen la función lcfirst o ucfirst, pero se puede usar la función charAt() con 0 como argumento para que devuelva el carácter ubicado en la posición 0. A éste carácter le aplicamos la funcion toUpperCase() o toLowerCase() según corresponda. Luego lo concatenamos con el resto del string y así nos queda la respuesta que esperamos.

// ejemplo de un ucfirst simulado
var cadena= "hola mundo!";
var respuesta= cadena.charAt(0).toUpperCase() + cadena.substr(1);
// nos queda "Hola mundo!"

// ejemplo de un lcfirst simulado
var cadena= "Hola Mundo!";
var respuesta= cadena.charAt(0).toLowerCase() + cadena.substr(1);
// nos queda "hola Mundo!"



sábado, 29 de agosto de 2015

Tomar una parte de un string

La función substr en Php

La función substr() en Php retorna la porción de un string entre una posición de comienzo y una longitud dada. Si la longitud es omitida devuelve todos los caracteres hasta el final del string. Ambos parámetros podrían ser negativos.

// Ejemplos 
$rest = substr("0123456"2);      // retorna "23456"
$rest = substr("0123456", -2);     // retorna "56"
$rest = substr("0123456"22);   // retorna "23"
$rest = substr("0123456"2, -2);  // retorna "234"
$rest = substr("0123456", -3, -2); // retorna "4"
$rest = substr("0123456", -3, -3); // retorna ""

La función substr en Perl

La función substr() en Perl retorna una porción del string contenida entre la posición de comienzo y una longitud dada. Si la longitud es omitida devuelve todos los caracteres hasta el final del string. Ambos parámetros podrían ser negativos.

# Ejemplos
my $cadena = "0123456";
my $subcadena = substr($cadena,0,4); # $subcadena contiene "0123";
$subcadena = substr($cadena,1,4);    # $subcadena contiene "1234";
$subcadena = substr($cadena,2);      # $subcadena contiene "23456";
$subcadena = substr($cadena,-4,3);   # $subcadena contiene "345";
$subcadena = substr($cadena,-4,-3);  # $subcadena contiene "3";
$subcadena = substr($cadena,3,-4);   # $subcadena contiene "";
$subcadena = substr($cadena,3,-3);   # $subcadena contiene "3";


La función substring en Javascript

La función substring() en Javascript retorna una porción del string contenida entre dos índices. Si alguno es negativo lo considera como un 0.

var cadena = "0123456";
var rest = cadena.substring( 2);  // retorna "23456"
rest = cadena.substring( -2);     // retorna "0123456"
rest = cadena.substring( 23);   // retorna "2"
rest = cadena.substring( 22);   // retorna ""
rest = cadena.substring( 2, -2);  // retorna "01"
rest = cadena.substring( 0, 2);   // retorna "01"
rest = cadena.substring( -3, -2); // retorna ""
rest = cadena.substring( -3, -3); // retorna ""

La función substr en Javascript

La función substr() en Javascript retorna una porción del string contenida desde una posición inicial y hasta la cantidad de caracteres indicada como segundo parámetro. Si la longitud es negativa, la considera como un 0. Si la posición inicial es negativa, toma la posición inicial desde el final del string en vez de desde el principio.

var cadena = "0123456";
var rest = cadena.substr( 2);   // retorna "23456"
rest = cadena.substr( -2);      // retorna "56"
rest = cadena.substr( 23);    // retorna "234"
rest = cadena.substr( 22);    // retorna "23"
rest = cadena.substr( 2, -2);   // retorna ""
rest = cadena.substr( 0, 2);    // retorna "01"
rest = cadena.substr( -3, -2);  // retorna ""
rest = cadena.substr( -3, -3);  // retorna ""

La función slice en Javascript

La función slice() en Javascript retorna una porción del string contenida entre dos índices. Si algún índice es negativo, toma la posición desde el final del string en vez de desde el principio.

var cadena = "0123456";
var rest = cadena.slice( 2);  // retorna "23456"
rest = cadena.slice( -2);     // retorna "56"
rest = cadena.slice( 2, 3);   // retorna "2"
rest = cadena.slice( 2, 2);   // retorna ""
rest = cadena.slice( 2, -2);  // retorna "234"
rest = cadena.slice( 0, 2);   // retorna "01"
rest = cadena.slice( -3, -2); // retorna "4"
rest = cadena.slice( -3, -3); // retorna ""


miércoles, 26 de agosto de 2015

Saber si un string está contenido dentro de otro string


La función index y rindex en Perl

La función index en Perl devuelve la posición de un substring dentro de un string. Si no lo encuentra devuelve -1. Tiene un tercer parámetro opcional que marca en qué posición comenzar la búsqueda. Por defecto comienza a buscar desde el inicio del string.
La función rindex busca la última posición del substring dentro de un string. El tercer parámetro opcional marca en qué posición comenzar la búsqueda, si no se da ninguna posición empieza por el final del string.

my $cadena = "Mauricio compra todo con tarjeta";
my ($subc1,$subc2) = ('compra','vende');
print index($cadena,$subc1); # imprime 9
print index($cadena,$subc1,10); # imprime -1
print index($cadena,$subc2); # imprime -1

Encontrar todos los substrings


my $abc = "Luis le prestó a Ana lo que Luisa no le pudo prestar a Luisana\n";
my $pos = length($abc) - 1; # Calcular la posición del último carácter 
print "Luis se encontró en las posiciones: ";

while(1)
{
  $pos = rindex($abc, "Luis", $pos);
  last if($pos< 0);
  print $pos--, " ";  # 55 28 0
}

print "\n";


La función strpos y strrpos en Php

La función strpos en Php devuelve la primer ocurrencia de un substring dentro de un string. Si no lo encuentra devuelve false. El valor 0 no debe confundirse con el valor false, 0 es la primer posición del string. Tiene un tercer parámetro opcional que indica desde qué posición empezar la búsqueda.
La función strrpos busca desde el final de la cadena hacia atrás. El tercer parámetro indica desde qué posición empezar a buscar y puede ser negativo, en ese caso cuenta la posición a buscar desde el final del string.

$mystring = 'Daniel sólo vende en efectivo';
$findme   = 'Dan';
$pos = strpos($mystring, $findme);

// Debe usar el operador !== . Usar != podría dar resultados inesperados
// porque la posición de 'Dan' es 0. La comparación (0 != false) evalúa
// a false.
if ($pos !== false) {
     echo "El string '$findme' fue encontrado en el string '$mystring'";
         echo " y existe en la posición $pos";
} else {
     echo "El string '$findme' no fue encontrado en el string '$mystring'";
}
$foo = "0123456789a123456789b123456789c";

var_dump(strrpos($foo, '7', -5));  // Comienza la búsqueda hacia atrás en la 5ta
                                   // posición desde el final. Resultado: int(17)

var_dump(strrpos($foo, '7', 20));  // Comienza la búsqueda desde la posición 20
                                   // del string. Resultado: int(27)

La función indexOf y lastIndexOf en Javascript

La función indexOf en Javascript devuelve la posición de un substring dentro de un string. Si no lo encuentra devuelve -1. Tiene un tercer parámetro opcional que marca en qué posición comenzar la búsqueda. Por defecto comienza a buscar desde el inicio del string.
La función lastIndexOf busca la última posición del substring dentro de un string. El tercer parámetro opcional marca en qué posición comenzar la búsqueda, si no se da ninguna posición empieza por el final del string.

// Ejemplo 1
var str = "Los teléfonos celulares se vuelven cada día más inteligentes";
var n = str.indexOf("e"); 
alert(n); // muestra un 5

// Ejemplo 2
var str = "La inteligencia no se puede medir con un número";
var n = str.lastIndexOf("z", 20); 
alert(n); // muestra un -1

domingo, 23 de agosto de 2015

Calcular la longitud de un string


Función length en Perl

La función length trae la longitud de un string.

#Ejemplo
print length("cadena"); # imprime 6


Función strlen en Php

La función strlen devuelve la longitud de un string.

$str 'abecedario';
echo strlen($str); // imprime 10


Propiedad length en Javascript

La propiedad length devuelve la longitud de un string.

var str = "Hello World!";
var n = str.length; // 12

jueves, 20 de agosto de 2015

Unir un array de strings en un solo string



Comando join en Perl

En Perl la función join une los elementos contenidos en un array y forma un único string, usa un separador opcional. Su primer argumento es el separador, el segundo es el array o incluso un hash.

# Ejemplo 1

my %personales = ('apellido'=>'Perez', 'email'=>'perez@example.com');
my $comma_separated = join(",", %personales);

print $comma_separated . "\n"; # email,perez@example.com,apellido,Perez

# Ejemplo 2

my @frutas = ("Banana", "Naranja", "Manzana", "Mango");
my $energy = join('',@frutas);

print $energy . "$/"; # BananaNaranjaManzanaMango 


Comando implode en Php

En Php la función implode une los elementos contenidos en un array y forma un único string, usa un separador opcional. Si no se especifica utiliza el carácter vacío.


// Ejemplo 1

$personales = array('apellido', 'email', 'dirección');
$comma_separated = implode(",", $personales);

echo $comma_separated; // apellido,email,dirección

// Ejemplo 2

$frutas = ["Banana", "Naranja", "Manzana", "Mango"];
$energy = implode($frutas);

alert($energy); // BananaNaranjaManzanaMango 


Comando join en Javascript

En Javascript la función join une los strings contenidos en un array y forma un único string, usa un separador opcional. Si no se especifica el separador, utiliza el carácter ','.

// Ejemplo 1

var personales = new Array('apellido', 'email', 'dirección');
var comma_separated = personales.join(",");

alert(comma_separated); // apellido,email,dirección

// Ejemplo 2

var frutas = ["Banana", "Naranja", "Manzana", "Mango"];
var energy = frutas.join();

alert(energy); // Banana,Naranja,Manzana,Mango 

martes, 18 de agosto de 2015

Separar un string por un delimitador



Comando split en Perl

En Perl la función split devuelve un array de strings como resultado de dividir un array por un carácter delimitador o por un string. Por ejemplo:

# Ejemplo 1
my $pizza  = "porcion1 porcion2 porcion3 porcion4 porcion5 porcion6";
my @porciones = split(" ", $pizza);
print $porciones[0]; # porcion1
print $porciones[1]; # porcion2

# Ejemplo 2
my $data = "foo:*:1023:1000::/home/foo:/bin/sh";
my ($user, $pass, $uid, $gid, $gecos, $home, $shell) = split(":", $data);
print $user; # foo
print $pass; # *


# Ejemplo 3
use Data::Dumper;

my $str = 'one,two,three,four';

# limite positivo
my @count = split(',', $str, 2);
print Dumper(\@count);

# limite negativo no tiene efecto
@count = split(',',$str,-1);
print Dumper(\@count);
$VAR1 = [
          'one',
          'two,three,four'
        ];
$VAR1 = [
          'one',
          'two',
          'three',
          'four'
        ];


Comando explode en Php

En Php la función explode devuelve un array de strings como resultado de dividir un array por un carácter delimitador o por un string. Por ejemplo:

Array
(
    [0] => one
    [1] => two,three,four
)
Array
(
    [0] => one
    [1] => two
    [2] => three
)


Comando split en Javascript

En Javascript la función split devuelve un array de strings como resultado de dividir un array por un carácter delimitador o por un string. Por ejemplo:

// Ejemplo 1
var pizza  = "porcion1 porcion2 porcion3 porcion4 porcion5 porcion6";
var porciones = pizza.split(" ");
alert(porciones[0]); // porcion1
alert(porciones[1]); // porcion2

// Ejemplo 2
var str = "Hola Mundo!";
// sin usar delimitador devuelve el mismo string
var res = str.split();
alert(res); // Hola Mundo! 


// Ejemplo 3
var str = 'one,two,three,four';

// limite positivo
console.log(str.split(',', 2));

// limite negativo no tiene efecto
console.log(str.split(',', -1));
["one", "two"]

["one", "two", "three", "four"]


sábado, 15 de agosto de 2015

Adivina qué número estoy pensando

Juego con Perl POE

En este juego el ordenador piensa un número del 1 al 20 y te da 5 oportunidades para adivinar. Si le erras te da una pista sobre si su número es mayor o menor al número que le dijiste. Para ponerlo más difícil hay un timeout de 10 segundos para que adivines el número o perderás el juego.

Por qué POE

Cuando un programa necesita interacción con el usuario o con datos externos, necesitamos una estructura de programa que sea más expresiva.
POE es un framework para la creación de programas en Perl donde se presentan naturalmente tareas que implican reaccionar ante datos externos o eventos, como las comunicaciones de red o las interfaces de usuario. Los programas en POE son no-lineales, se configuran un montón de pequeñas subrutinas y se define la forma en que se llaman entre sí. POE cambiará automáticamente entre ellas mientras maneja la entrada y salida del programa.

#!/usr/bin/perl -w
use strict;

# Usar POE!
use POE qw/Wheel::ReadLine/;

# Necesitas hacer esto para Cygwin
#$/="\n\r";

# flush STDOUT automaticamente
$|++;


  POE::Session->create(
    inline_states =>
      { _start    => \&handler_start,
        gotLine   => \&handler_gotLine,
        prompt    => \&handler_prompt,
        gameover    => \&handler_gameover,
        timeout   => \&handler_timeout,
        #_stop     => \&handler_stop,
      }
  );

my $limite= 20;
my $intentos_total= 5;
my $intentos= $intentos_total;
my $numero= int(rand($limite-1)) + 1;
print "Adivina el numero$/";
print "Hola!!!, estoy pensando un número del 1 al $limite. ";
print "Tienes $intentos oportunidades para adivinarlo$/";
$poe_kernel->run();
print "\n";

sub handler_start {
  my ($kernel, $heap, $session) = @_[KERNEL, HEAP, SESSION];

  # POE::Wheel::ReadLine obtiene la entrada de tu terminal y viene con un historial
  # cuando una nueva linea es ingresada, se ejecuta el evento gotLine
  $heap->{wheel} = POE::Wheel::ReadLine->new
      (
        InputEvent => 'gotLine',
    );

  # pide por el evento prompt para que se ejecute otra vez
  $kernel->yield('prompt');
}
sub handler_prompt {
  my ($kernel, $heap, $session) = @_[KERNEL, HEAP, SESSION];
  my $tiempo= 10;
  print "Tienes $tiempo segundos para adivinar, o perderás el juego!$/";
  if (2>$intentos){ 
    print "Es tu última oportunidad$/";
  }
  $heap->{wheel}->get('Tipea rápido: ');

  # dispara el evento timeout para dentro de una cantidad de segundos
  $kernel->delay('timeout',$tiempo);
}
sub handler_timeout {
  my ($kernel, $heap, $session) = @_[KERNEL, HEAP, SESSION];

  # informar al usuario que perdió
  print "$/El número era $numero, tardaste demasiado tiempo, game over$/";

  # setear esta variable a undef hará que termine nuestro Wheel
  # sin wheel y sin el evento delay pendiente la cola del kernel
  # está vacia y se realiza un shutdown
  $heap->{wheel}=undef;
}

sub handler_gameover {
  my ($kernel, $heap, $session) = @_[KERNEL, HEAP, SESSION];

  # informar al usuario que perdió
  print "\nEl número era $numero, lo siento, game over\n";

  # setear un delay sin el número de timeout limpia el evento
  # sin el evento delay pendiente la cola del kernel
  # está vacia y se realiza un shutdown
  $kernel->delay('timeout');
}

sub handler_gotLine {
  my ($kernel, $heap, $session, $arg, $exception) = 
            @_[KERNEL, HEAP, SESSION, ARG0, ARG1];

  if(defined $arg) {
    $heap->{wheel}->addhistory($arg);
    $intentos--;
    if ($numero==$arg){
      print "Excelente! Mi número era el $numero. Lo conseguiste en " . ($intentos_total - $intentos) . " intentos!$/";
      # setear un delay sin el número de timeout limpia el evento
      $kernel->delay('timeout');
      # setear esta variable a undef hará que termine nuestro Wheel
      $heap->{wheel}=undef;
      return;
    }elsif($numero>$arg){
      print "El número es mayor que $arg$/";
    }else{
      print "El número es menor que $arg$/";
    }
    if (1>$intentos){
      # se acabaron las oportunidades para adivinar el número
      $kernel->yield('gameover');
      return;
    }
  }
  else {
    print "Got input exception '$exception'.  Exiting...$/";

    # setear esta variable a undef hará que termine nuestro Wheel
    $heap->{wheel}=undef;
    # setear un delay sin el número de timeout limpia el evento
    $kernel->delay('timeout');

    return;
  }

  # ejecuto el evento prompt de nuevo para resetear el timeout e
  # imprimir un nuevo prompt
  $kernel->yield('prompt');
}

Salida


Adivina el numero
Hola!!!, estoy pensando un número del 1 al 20. Tienes 5 oportunidades para adivinarlo

Tienes 10 segundos para adivinar, o perderás el juego!
Tipea rápido: 8
El número es mayor que 8
Tienes 10 segundos para adivinar, o perderás el juego!
Tipea rápido: 18
El número es menor que 18
Tienes 10 segundos para adivinar, o perderás el juego!
Tipea rápido: 
              El número era 14, tardaste demasiado tiempo, game over
Adivina el numero
Hola!!!, estoy pensando un número del 1 al 20. Tienes 5 oportunidades para adivinarlo

Tienes 10 segundos para adivinar, o perderás el juego!
Tipea rápido: 1
El número es mayor que 1
Tienes 10 segundos para adivinar, o perderás el juego!
Tipea rápido: 2
El número es mayor que 2
Tienes 10 segundos para adivinar, o perderás el juego!
Tipea rápido: 3
El número es mayor que 3
Tienes 10 segundos para adivinar, o perderás el juego!
Tipea rápido: 4
El número es mayor que 4
Tienes 10 segundos para adivinar, o perderás el juego!
Es tu última oportunidad
Tipea rápido: 5
El número es mayor que 5

El número era 13, lo siento, game over
Adivina el numero
Hola!!!, estoy pensando un número del 1 al 20. Tienes 5 oportunidades para adivinarlo

Tienes 10 segundos para adivinar, o perderás el juego!
Tipea rápido: 10
El número es mayor que 10
Tienes 10 segundos para adivinar, o perderás el juego!
Tipea rápido: 15
Excelente! Mi número era el 15. Lo conseguiste en 2 intentos!

Desafío

Como tarea final te dejo una modificación para que al finalizar el juego te pregunte que si quieres volver a jugar, y que puedas jugarlo otra vez.

miércoles, 12 de agosto de 2015

Intervalos de horas que se superponen

Problema

Supongamos que tenemos una lista de horarios con hora de comienzo y de final. Y queremos saber si algún intervalo horario se superpone con algún otro. Los horarios vienen en formato 'HH:mm:ss' pero solo nos interesan las horas y los minutos. Supongamos también que está en formato de 24hs.
Deberíamos hacer una serie de consultas con cada intervalo, pero como puede haber intervalos que cruzan las 00hs, esas consultas se vuelven muy complicadas. Para simplificar, supongamos que sólo queremos saber si la hora de comienzo de un intervalo está superpuesta en algún otro intervalo. Por lo tanto si esa hora es mayor o igual al inicio de un intervalo pero menor estricto que el final de ese intervalo se considera que el intervalo examinado se encuentra superpuesto con el otro.

Por ejemplo el intervalo '21:00:00,23:00:00' se superpone con los intervalos '20:00:00,22:00:00', '22:00:00,01:30:00' y '09:00:00,21:00:00', pero no se superpone con el intervalo '23:00:00,04:00:00' ni con el intervalo '13:00:00,18:00:00'.



Solución

A continuación presento un script en Perl para solucionar este problema. Dado un array de intervalos y un índice que es el índice del array donde está el intervalo que quiero comparar con todos los demás, ejecuto una función que me retorna si se superpone con alguno de los otros intervalos.

#!/usr/bin/perl
use strict;
use Data::Dumper;

my @intervalos;
push(@intervalos,{'desde'=>'09:00:00','hasta'=>'12:00:00'});
push(@intervalos,{'desde'=>'12:00:00','hasta'=>'15:00:00'});
push(@intervalos,{'desde'=>'14:00:00','hasta'=>'17:00:00'});
push(@intervalos,{'desde'=>'17:30:00','hasta'=>'22:30:00'});
push(@intervalos,{'desde'=>'22:00:00','hasta'=>'00:00:00'});
push(@intervalos,{'desde'=>'23:59:00','hasta'=>'08:59:00'});

print Dumper(\@intervalos);

my $indice= 5;
my $es_superpuesto= ver_solapacion($indice,@intervalos);
print "Superpuesto: $es_superpuesto\n";

sub ver_solapacion{
  my ($indice,@intervalos)= @_;
  my $s= 0;
  my $pivote_indice= $intervalos[$indice]->{'desde'};
  print "Hora 'desde' del intervalo interesante: $pivote_indice \n";
  my $pivote= time_trans($pivote_indice);
  my $i= 0;
  foreach my $row (@intervalos){
    if ($i==$indice){ $i++; next; }
    my $a= time_trans($row->{'desde'});
    my $b= time_trans($row->{'hasta'});
    my $c= time_suma($a,$b);
    my $d= time_suma($a,$pivote);
    if ($d < $c){ $s=1; last; }
    $i++;
  }
  return $s;
}
sub time_trans{
  my ($hora)= @_;
  my @h= split(':',$hora);
  return  $h[0] + ($h[1]/100);
}
sub time_suma{
  my ($a,$b)= @_;
  if ($a>$b){ return 24 + $b; }
  return $b - $a;
}

Resultado: el intervalo está superpuesto

$intervalos = [
          {
            'hasta' => '12:00:00',
            'desde' => '09:00:00'
          },
          {
            'hasta' => '15:00:00',
            'desde' => '12:00:00'
          },
          {
            'hasta' => '17:00:00',
            'desde' => '14:00:00'
          },
          {
            'hasta' => '22:30:00',
            'desde' => '17:30:00'
          },
          {
            'hasta' => '00:00:00',
            'desde' => '22:00:00'
          },
          {
            'hasta' => '08:59:00',
            'desde' => '23:59:00'
          }
        ];
Hora 'desde' del intervalo interesante: 23:59:00 
Superpuesto: 1

sábado, 8 de agosto de 2015

Clase Cuenta Bancaria en JS usando Joose


Problema

Modelizaremos una cuenta bancaria normal. En esta cuenta se puede depositar dinero, retirar dinero y comprobar su saldo actual. No se puede retirar más dinero que el que hay en la cuenta. Además modelizaremos una cuenta corriente con una protección contra sobregiros opcional. La protección contra sobregiros protegerá al propietario de la cuenta corriente retirando automáticamente los fondos necesarios de la cuenta de sobregiro para asegurar que un cheque no rebotará.

Solución

Module("Banco", function (m) {
  Class("Cuenta", {
    has: {
      balance: {
        is: "rw",
        init: 0
      }
    },
    methods: {
      depositar: function (monto) {
        this.setBalance(this.getBalance() + monto)
      },
      retirar: function (monto) {
        if(this.getBalance() < monto) {
          throw "Cuenta sobregirada"
        }
        this.setBalance(this.getBalance() - monto);
        return this.getBalance();
      }
    }
  });
  
  Class("CuentaCorriente", {
    isa: m.Cuenta,
    has: {
      cuentaSobregiro: {
        isa: m.Cuenta,
        is: "rw"
      }
    },
    before: {
      retirar: function (monto) {
        var cantidadEnRojo = monto - this.getBalance()
        
        if(this.cuentaSobregiro && cantidadEnRojo > 0) {
           this.cuentaSobregiro.retirar(cantidadEnRojo);
           this.depositar(cantidadEnRojo);
        }
      }    
    }
  })
}) 

Explicacion

La clase Cuenta tiene un "balance" inicializado en 0 y dos métodos: "depositar" y "retirar". La CuentaCorriente es una subclase de Cuenta que tiene una "cuentaSobregiro" opcional que es de tipo Cuenta. Antes de retirar un monto de la CuentaCorriente verifica si puede sobregirar la cuenta.

Módulos

Module("Banco", function (m) {
...
} 

La función Module() crea el namespace "Banco". Toma como segundo argumento una función que crea las clases Cuenta y CuentaCorriente. La función obtiene un objeto módulo como parámetro, que más tarde será utilizado para facilitar el acceso a otras clases ubicadas dentro del namespace.

Restricciones de Tipo

has: {
  cuentaSobregiro: {
    isa: m.Cuenta,
    is: "rw"
  }
}, 

Aquí introducimos un nuevo atributo para la clase CuentaCorriente llamado "cuentaSobregiro". Usando la palabra clave isa ponemos una restricción de tipo sobre el atributo. Esta restricción es chequeada cada vez que el método setCuentaSobregiro() es usado. Ese método fue creado automáticamente cuando se definió el atributo como rw.

Modificador de método "before"

before: {
  retirar: function (monto) {
    var cantidadEnRojo = monto - this.getBalance()
    
    if(this.cuentaSobregiro && cantidadEnRojo > 0) {
       this.cuentaSobregiro.retirar(cantidadEnRojo);
       this.depositar(cantidadEnRojo);
    }
  }    
} 

Introducimos un método que será ejecutado antes del método retirar(). De esta manera el método original se ve aumentado en su comportamiento. Si el dinero actual en el balance de la cuenta no es suficiente para el retiro, se extrae lo que falta de la cuentaSobregiro y se deposita en la cuenta.

Modificador de método "override"

Otra posible implementación podría haber sido sobreescribir el método retirar() usando el modificador override, el cual permite llamar al método de la superclase haciendo this.SUPER()
override: {
  retirar: function (monto) {
    var cantidadEnRojo = monto - this.getBalance()
      
    if(this.cuentaSobregiro && cuentaSobregiro > 0) {                
      this.cuentaSobregiro.retirar(cantidadEnRojo);
      this.depositar(cantidadEnRojo);
    }

    this.SUPER(amount)
  }
} 

Instanciación de los objetos

Todos los objetos Joose reciben un inicializador estándar que permite inicializar los objetos usando argumentos por nombre en el constructor:
var cajaAhorros= new Banco.Cuenta({ balance: 100 });
var cuentaCorriente= new Banco.CuentaCorriente({ 
    balance:         200, 
    cuentaSobregiro: cajaAhorros
});
console.log("Una cuenta: ");
console.log(cajaAhorros);
console.log("Una cuenta corriente: ");
console.log(cuentaCorriente);
console.log("Operamos en la cuenta con un retiro parcial de 100");
cuentaCorriente.retirar(100);
console.log(cuentaCorriente);
console.log(cajaAhorros);
console.log("Sobregiramos la cuenta con un retiro mayor: 150");
cuentaCorriente.retirar(150);
console.log(cuentaCorriente);
console.log(cajaAhorros);
console.log("Sobregiramos demasiado la cuenta: 300");
cuentaCorriente.retirar(300);

Una cuenta: 
 a Banco.Cuenta { balance=100, meta=a Joose.Class, initialize=initialize(), más...}
Una cuenta corriente: 
 a Banco.CuentaCorriente { balance=200, cuentaSobregiro=a Banco.Cuenta, meta=a Joose.Class, más...}
Operamos en la cuenta con un retiro parcial de 100
 a Banco.CuentaCorriente { balance=100, cuentaSobregiro=a Banco.Cuenta, meta=a Joose.Class, más...}
 a Banco.Cuenta { balance=100, meta=a Joose.Class, initialize=initialize(), más...}
Sobregiramos la cuenta con un retiro mayor: 150
 a Banco.CuentaCorriente { balance=0, cuentaSobregiro=a Banco.Cuenta, meta=a Joose.Class, más...}
 a Banco.Cuenta { balance=50, meta=a Joose.Class, initialize=initialize(), más...}
Sobregiramos demasiado la cuenta: 300
uncaught exception: Cuenta sobregirada

lunes, 3 de agosto de 2015

Web API Console

Comandos


console.log(object[, object, …])
Escribe un mensaje en la consola. Como primer argumento puede aceptar un objeto o un string con especificadores de formato. Por ejemplo %s para string, %d para enteros, %f para flotantes o %o para objetos.
console.log("Usuario %s posee %d créditos", userName, creditCount);

console.debug(object[, object, ...])
Idéntico a console.log.

console.info(object[, object, ...])
Idéntico a console.log pero agrega un ícono "info".

console.warn(object[, object, ...])
Idéntico a console.log pero agrega un ícono "warning".

console.error(object[, object, ...])
Similar a console.log pero agrega un ícono "error" y una traza de la pila.

console.assert(expression[, object, ...])
Testea que una expresión sea verdadera. Si no lo es escribirá un mensaje de error en la consola junto a una traza de la pila. Por ejemplo:
console.assert(list.childNodes.length < 10, "List item count is >= 10");

console.clear()
Limpia la consola.

console.dir(object)
Muestra las propiedades del objeto.

console.trace()
Muestra una traza de la pila en el punto donde es llamado.

console.time(label)
Comienza un timer con una etiqueta label asociada. Cuando console.timeEnd(label) es llamado, el timer es detenido y se muestra en la consola el tiempo transcurrido.

console.profile([title])
Inicia el CPU profile con un título opcional. Para completar el profile llame a console.profileEnd() para que imprima el reporte.

console.count([label])
Escribe el número de veces count() fue ejecutado con el mismo label o las veces que se ejecutó la misma línea de código donde count() fue llamado y ejecutado.

console.table(data[, columns])
Similar a console.log pero muestra los datos en formato de tabla. Por ejemplo:
var languages = [
    { name: "JavaScript", fileExtension: ".js" },
    { name: "Perl", fileExtension: ".pl" },
    { name: "Python", fileExtension: ".py" },
    { name: "Erlang", fileExtension: ".erl" },
    { name: "PHP", fileExtension: ".php" }
];

console.table(languages);
Muestra por consola:

name fileExtension
"JavaScript" ".js"
"Perl" ".pl"
"Python" ".py"
"Erlang" ".erl"
"PHP" ".php"

domingo, 2 de agosto de 2015

HTML DOM querySelector()

Ejemplo

document.querySelector(".ejemplo");
Obtiene el primer elemento en el documento con la clase "ejemplo".
El método querySelector() retorna el primer elemento del documento que matchea con un específico selector CSS. Si se quiere obtener todos los elementos se puede usar querySelectorAll().

Sintaxis

document.querySelector(CSS selectors)
El parámetro especifica uno o más selectores CSS separados por comas, para buscar un elemento en el documento. Retorna un objeto NodeList o null si no es encontrado.

Más ejemplos

document.querySelector("p");
Obtiene el primer elemento párrafo en el documento.
document.querySelector("p.ejemplo");
Obtiene el primer elemento párrafo en el documento con la clase "ejemplo".
document.querySelector("#demo").innerHTML = "Hola Mundo!";
Obtiene el primer elemento con id="demo" y modifica su texto.
document.querySelector("div > p");
Obtiene el primer elemento párrafo cuyo parent es un elemento div.
document.querySelector("a[target]");
Obtiene el primer elemento <a> que tenga un atributo "target".

Ejemplo con múltiples selectores

<h2>Titulo de nivel 2</h2>
<h3>Titulo de nivel 3</h3>

document.querySelector("h2, h3").style.backgroundColor = "red";
El primer elemento que encuentra es un título de nivel 2, por eso lo colorea de rojo y no sigue buscando elementos de título de nivel 3.
En el ejemplo siguiente colorea el elemento de título de nivel 3 porque es el primer elemento que matchea con alguno de los selectores, luego no sigue buscando.
<h3>Titulo de nivel 3</h3>
<h2>Titulo de nivel 2</h2>

document.querySelector("h2, h3").style.backgroundColor = "red";

sábado, 1 de agosto de 2015

Clase Selfie en Joose JS

Código

Class("Fotografia", {
    has: {
        alto: {is: "ro"},
        ancho: {is: "rw"},
    },
    methods: {
        clear: function () {
            this.alto = 0;
            this.setAncho(0);
        }
    }
})
Class("Selfie", {
    isa: Fotografia,
    has: {
        etiqueta: {init: "yo"}
    },
    after: {
        clear: function () {
            this.etiqueta = "yo";
        }
    }
})
var foto= new Selfie({ancho: 360, alto:240});
foto.etiqueta= "Ricky Martin";

Explicación


Este es un ejemplo de Joose para crear una clase que hereda de otra. La función Class() toma dos parámetros: el nombre de la clase y la definición de la clase pasada como un objeto literal. La sintaxis para definir la clases es declarativa y fácil de leer. Las palabras clave como "has", "methods", "isa" y "after" son usadas para expresar diferentes aspectos de la clase.

Atributos


has: {
        alto: {is: "ro"},
        ancho: {is: "rw"},
    }, 

El primer elemento de la declaración de la clase Fotografía es un bloque "has" que define dos atributos de la clase: alto y ancho. El bloque alto: {is: "ro"} define "alto" como de sólo lectura. Mientras que el bloque ancho: {is: "rw"} define "ancho" como un atributo de lectura y escritura. Joose automáticamente crea los métodos getAlto() para "alto" y getAncho() y setAncho() para "ancho". El nombre de estos métodos sigue la convención camelCase, colocando la primera letra del atributo en mayúscula.

has: {
        etiqueta: {init: "yo"}
    }, 

El bloque etiqueta: {init: "yo"} define el atributo "etiqueta" con un valor por default que es el string "yo". Como no se especifica si es rw o ro, por defecto no se crean los métodos de acceso getEtiqueta() y setEtiqueta().

Métodos


methods: {
        clear: function () {
            this.alto = 0;
            this.setAncho(0);
        } 

El bloque "methods" define el método clear() para la clase Fotografía. Podemos poner todos los métodos que queramos. En este caso la función setea "alto" accediendo a la variable directamente, pero modifica "ancho" usando su método modificador creado con la declaración "rw".

Herencia


 isa: Fotografia, 

La cláusula "isa" en la definición de Selfie define que su super clase es Fotografía. Selfie hereda los métodos y atributos de su superclase.

Modificador de método


after: {
        clear: function () {
            this.etiqueta = "yo";
        }
    } 

Joose soporta los llamados modificadores de métodos. Uno de los 5 modificadores que declaran métodos con un comportamiento especial es "after". Aquí define que acciones ejecutar después de llamar al método con el mismo nombre definido en su superclase.

Inicialización


var foto= new Selfie({ancho: 360, alto:240}); 

El objeto literal {ancho: 360, alto:240} es pasado como argumento de un método especial llamada initialize() que lo usa para inicializar los valores del atributo del nuevo objeto.


viernes, 31 de julio de 2015

Construir un módulo Joose JS

Module(name,function)

La función global Module(name,function) hace que crear un namespace para las clases, roles y prototipos sea muy fácil. El módulo automáticamente crea el namespace llamado "name". Las clases que son creadas dentro de la función "function" serán puestas automáticamente dentro del namespace del módulo. Esto reduce el riesgo de conflictos de nombres. Un módulo proporciona una buena forma de estructurar y organizar el código que compone un proyecto (múltiples módulos) o una librería (normalmente un único módulo).

Module("com.test.module", function (m) {
    Class("Test", {
        methods: { world: function () { return "hello" } }
    });
    Class("Test2", {
        methods: { world: function () { return "hello" } }
    })
})

En el ejemplo la clase Test será globalmente conocida bajo el nombre "com.test.module.Test" y así se reduce el riesgo de conflictos de nombres.

jueves, 30 de julio de 2015

Construir una clase Joose JS

La función global Class(name, properties) crea una clase con el nombre "name". La clase es inicializada usando sus propiedades "properties" de esta manera:
  • isa: Indica de qué clase hereda, o sea, su superclase
  • does: Aplica roles a la clase
  • has: Crea atributos para la clase
  • methods: Crea los métodos de instancia
  • classMethods: Crea los métodos de clase
Existen también modificadores de métodos: before, after, around, override, augment

isa

Class("Avion", {
 isa: Transporte
})

En el ejemplo Avion heredará todos los métodos de la clase Transporte. No hay Herencia Múltiple.

does

Class("Pintura", {
 does: Coloreable
})

Aplica roles, a veces llamado traits (algo similiar a mixins), a la clase. Para aplicar múltiples roles se le pasa un array de roles.

has

Class("Puerta",{
 has: {
  color: {
   is: "rw",
   init: "blanca"
  },
  material: {
   is: "ro",
   init: "madera"
  }
 }
}

En el ejemplo la clase Puerta tiene dos atributos: color y material. El atributo "color" es de lectura/escritura y es inicializado a "blanca". El atributo "material" es de sólo lectura y es inicializado a "madera". Joose genera los métodos de acceso a los atributos automáticamente, en este caso se llamarían: getColor(), setColor(), getMaterial(). No hay un método setMaterial() porque fue declarado de sólo lectura.

methods

methods: {
 clear: function(){
  this.setX(0);
  this.setY(0);
 },
 stringify: function(){
  return "" + this.getX() + "," + this.getY()
 }
}

En el ejemplo agregamos dos métodos a la clase, los métodos clear() y stringify(). El primero para limpiar los atributos de la clase y el segundo para devolver un string que representa los valores de sus atributos.

classMethods

classMethods: {
 makeNew: function(){
  return new Punto3D()
 }
}

Funciona como "methods" pero para generar métodos de clase en vez de métodos de instancia. Ahora podemos crear un Punto3D llamando a Punto3D.makeNew()

Modificadores de métodos


Los métodos declarados como "before" son llamados antes del método que sobreescriben. El valor de retorno es descartado.
before: {
 clear: function(){ alert("Antes: " + this.stringify()) }
}

Los métodos declarados como "after" son llamados después del método que sobreescriben. El valor de retorno es descartado.
after: {
 clear: function(){ alert("Después: " + this.stringify()) }
}

Métodos especiales


Los objetos Joose devuelven el string "a ClassName" cuando se llama al método stringify(), siendo ClassName el nombre de la clase a la que pertenece el objeto. Éste método se puede sobreescribir. Algunos engines Js utilizan el método toString(), por eso no se modifica directamente toString(), Joose utiliza stringify().
Existe el método initialize() que Joose automáticamente utiliza para inicializar una instancia como esta: var point = new Point({x:10, y: 10})
Se lo puede sobreescribir para cambiar ese comportamiento. O podría ser útil definir un modificador "after" para el método initialize().

miércoles, 29 de julio de 2015

Clase Punto3D en Javascript usando Joose

Javascript


En Javascript vamos a implementar una clase Punto3D que hereda de la clase Punto implementada antes aquí.
// Implementamos una función para herencia
function inherit(superClass, subClass) {
    for(var i in superClass.prototype) {
        subClass.prototype[i] = superClass.prototype[i]
    }
}
Test.StandardPoint3D = function (x, y, z) {
    this.x = x || 0
    this.y = y || 0
    this.z = z || 0
}
// Hacemos que Test.Standard sea la super clase de Test.StandardPoint3D
inherit(Test.StandardPoint, Test.StandardPoint3D)
// No podemos asignar un nuevo prototipo porque ya tenemos uno de la super clase
Test.StandardPoint3D.prototype.getZ = function () {
    return this.z
}
Test.StandardPoint3D.prototype.setZ = function (z) {
    this.z = z;
}
var superMethod = Test.StandardPoint3D.prototype.clear;
Test.StandardPoint3D.prototype.clear = function () {
    superMethod.apply(this);
    this.z = 0;
}


Joose


Ahora reimplementamos la clase Punto3D usando la librería Joose y vemos que el nuevo código es más compacto y legible porque se ocupa de lo que nos importa.
Module("Test", function (m) {
    Class("Point3D", {
        isa: m.Point,
        has: {
            z: {
                is: "rw",
                init: 0
            }
        },
        after: {
            clear: function () {
                this.setZ(0)
            }
        }
    })
})

domingo, 26 de julio de 2015

Ejemplo de código Joose

Descargamos la libreria joose.js de https://code.google.com/p/joose-js/downloads/list y creamos este pequeño ejemplo llamado runpoint.html:


<!DOCTYPE html>
<html>
 <head>
  <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" >
  <script type="text/javascript" src="./joose.js"></script>
  <title>Joose Point</title>

  <script type="text/javascript">
  Class("Punto", {
   has: {
    x: {is: "rw"},
    y: {is: "rw"}
   },
   methods: {
    clear: function () {
     this.setX(0);
     this.setY(0);
    }
   }
  });

  Class("Punto3D", {
   isa: Punto,
   has: {
    z: {is: "rw"}
   },
   after: {
    clear: function () {
     this.setZ(0);
    }
   }
  });
  </script>

 </head>
 <body>

  <script type="text/javascript">
  var point = new Punto3D();
  point.setX(10);
  point.setY(30);
  var y = point.getY();
  point.z = 1;
  console.log("Un punto: ");
  console.log(point);
  console.log("var y: " + y);
  point.clear();
  console.log("Coordenada z luego de clear: " + point.getZ());
  var point2 = new Punto3D({ x: 10, y: 20});
  console.log("Coordenada y de point2: " + point2.y);
  </script>

 </body>
</html>

La salida en la consola del navegador es algo como esto:



runpoint.html:41 Un punto:
runpoint.html:42 f {x: 10, y: 30, z: 1}
runpoint.html:43 var y: 30
runpoint.html:45 Coordenada z luego de clear: 0
runpoint.html:47 Coordenada y de point2: 20

Clase Punto en Javascript usando Joose

Javascript


En Javascript vamos a implementar una clase Punto con los atributos x e y usando el namespace Test.
if(Test == null) {
    Test = {};
}
Test.StandardPoint = function (x, y) {
    this.x = x || 0
    this.y = y || 0
}
Test.StandardPoint.prototype = {
    getX: function () {
        return this.x
    },
    setX: function (x) {
        this.x = x
    },
    getY: function () {
        return this.y
    },
    setY: function (y) {
        this.y = y;
    },
    clear: function () {
        this.setX(0)
        this.setY(0)
    }
}

Joose


Ahora reimplementamos la clase Punto usando la librería Joose y vemos que el nuevo código es más corto y fácil de leer.
Module("Test", function (m) {
    Class("Point", {
        has: {
            x: {
                is:   "rw",
                init: 0
            },
            y: {
                is:   "rw",
                init: 0
            }
        },
        methods: {
            clear: function () {
                this.setX(0);
                this.setY(0);
            }
        }
    })
})

jueves, 23 de julio de 2015

Pequeña introducción a POE

POE es un framework escrito en Perl. POE permite escribir programas que manejan entradas desde múltiples fuentes asíncronas. Éstas son grandes palabras, pero sólo significa que obtendrás la información cuando esté disponible, y no tendrás que preocuparte por quedarte esperando.

La cooperación se logra creando un conjunto de estados, los cuales son invocados por eventos. Los eventos son generados por los motores de entradas (llamados aquí Wheels), por los timers o por otros estados.
En el corazón de POE se encuentra el POE kernel. Mantiene una cola de eventos programados y utiliza la funcionalidad select o poll del Sistema Operativo para ver la actividad de los sockets o manejadores de archivos que te interesan. Cuando sea el tiempo de disparar un evento, o hay datos disponibles, el manejador del estado asociado es invocado. Otros event loops están también disponibles, haciendo posible tener programas POE que tengan interfaces de usuario Tk o curses, por ejemplo.

Ejemplo: cómo hacer una entrada de datos con timeout

En este tutorial veremos cómo ponerle un timeout a la STDIN. Veremos que es fácil escribir aplicaciones interactivas en POE con edición en la línea de comandos y con historial.
El primer paso en cualquier programa POE es usar el módulo POE. Como los programas POE a menudo necesitan usar varios módulos del namespace POE::, tú puedes hacer

use POE qw/Wheel::ReadLine Component::Client::HTTP::SSL/;

como un atajo para

use POE;
use POE::Wheel::ReadLine;
use POE::Component::Client::HTTP::SSL;

En nuestro ejemplo sólo necesitamos POE::Wheel::ReadLine, el cual maneja nuestros requerimientos de entrada.
Cada programa consiste en una o más sesiones POE, las cuales mantienen un conjunto de estados.

#!/usr/bin/perl -w
use strict;

# Use POE!
use POE qw/Wheel::ReadLine/;

# Need to do this on Cygwin
#$/="\n\r";

# flush STDOUT automatically
$|++;


  POE::Session->create(
    inline_states =>
      { _start    => \&handler_start,
        gotLine   => \&handler_gotLine,
        prompt    => \&handler_prompt,
        timeout   => \&handler_timeout,
        #_stop     => \&handler_stop,
      }
  );

En el constructor de la sesión especificamos un hash de nombres de estados, y los manejadores asociados a esos estados. Las subrutinas pueden tener nombre, como aquí, o ser referencias a subrutinas anónimas.
Los estados _start y _stop son especiales, ellos son invocados por el kernel cuando la sesión es creada, o justo antes de ser destruída.
En este caso no necesitamos hacer nada especial para manejar el _stop, así que este estado es comentado y su manejador no es implementado.
El manejador del _start será invocado antes que POE::Session->create() retorne.
El próximo paso es hacer andar el kernel, y salir del programa una vez hecho esto.

$poe_kernel->run();
exit;

$poe_kernel es exportado por POE automáticamente.

Manejadores

De acuerdo, no hemos definido ningún manejador todavía, así que nuestro programa aún no compila, ni siquiera corre.
Cada manejador recibirá un largo número de argumentos en @_. Los más interesantes son el heap, el kernel y el session asociados con el estado.
El heap es solo un escalar, casi siempre una referencia a hash.
Éstos valores pueden ser accedidos usando un array slice sobre @_ para inicializar variables, o explícitamente referenciadas por $_[HEAP], $_[KERNEL], $_[SESSION]. POE exporta HEAP, KERNEL, SESSION y otras constantes varias en forma automática.

El primer manejador que veremos es nuestro manejador de start:

sub handler_start {
  my ($kernel, $heap, $session) = @_[KERNEL, HEAP, SESSION];

  # POE::Wheel::ReadLine gets you terminal input with command line
  # history.  whenever a line is input, the 'gotLine' event
  # will run
  $heap->{wheel} = POE::Wheel::ReadLine->new
      (
        InputEvent => 'gotLine',
    );

  # ask for the prompt event to get run next
  $kernel->yield('prompt');
}

Wheels

Los módulos POE::Wheel son los que manejan el pegamento molesto de nuestros generadores externos de eventos, tales como sockets o manejadores de archivo, hacia los estados POE.
POE::Wheel::ReadLine es el que invoca estados cuando los datos son entrados por la consola. También maneja la edición y el historial en la línea de comandos, con un poco de ayuda de nosotros.
Note que salvamos el wheel en nuestro hash %{$heap}. De otro modo el wheel sería inmediatamente destruído, desde que no habría referencias hacia él. Usaremos este truco de nuevo más tarde, cuando sea tiempo de salir. Por ahora asociamos el InputEvent del wheel con el estado 'gotLine' (manejado por la subrutina _gotLine). Entonces usamos el "yield" para pedirle al kernel que ejecute el estado prompt lo antes posible.

sub handler_prompt {
  my ($kernel, $heap, $session) = @_[KERNEL, HEAP, SESSION];

  print "You have 10 seconds to enter something, or I'll quit!$/";
  $heap->{wheel}->get('Type fast: ');

  # this will make the timeout event fire in 10 seconds
  $kernel->delay('timeout',10);
}

En este manejador usamos el método get en nuestro wheel para pedirle una entrada al usuario y programamos un timeout de 10 segundos.
Aún si no es la primera vez que el manejador es invocado, la llamada a delay() elimina el evento anterior y programa uno nuevo de 10 segundos.
A partir de aquí las cosas están en manos del kernel. Si el usuario no hace nada en 10 segundos (más o menos, los tiempos de espera son aproximados) el estado de timeout será activado.

sub handler_timeout {
  my ($kernel, $heap, $session) = @_[KERNEL, HEAP, SESSION];

  # taunt (or inform) the user
  print "You took too long, game over$/";

  # setting this to undef will make our Wheel get shutdown
  # with no wheel, and no pending delay event, the kernel
  # queue is empty resulting in shutdown
  $heap->{wheel}=undef;
}

Cuando el atributo wheel es puesto undef en la subrutina handler_timeout, el wheel es destruído, y dado que no hay eventos pendientes ni otras fuentes de eventos, el kernel termina.
Si el usuario entra algo por consola o presiona Ctrl-C, el manejador gotLine es llamado.


sub handler_gotLine {
  my ($kernel, $heap, $session, $arg, $exception) = 
            @_[KERNEL, HEAP, SESSION, ARG0, ARG1];

  if(defined $arg) {
    $heap->{wheel}->addhistory($arg);
    print "Very good, you entered '$arg'.  You get another 10 seconds.$/";
  }
  else {
    print "Got input exception '$exception'.  Exiting...$/";

    # setting this to undef will make our Wheel get shutdown
    $heap->{wheel}=undef;
    # setting a delay without a timeout number clears the event
    $kernel->delay('timeout');

    return;
  }

  # run the prompt event again to reset timeout and print
  # new prompt
  $kernel->yield('prompt');
}

Una cosa interesante aquí es que leemos ARG0 y ARG1 de @_. POE pasa argumentos en @_ en el rango ARG0 hasta $#_. En el caso de un InputHandler para este wheel, ARG0 será el texto de la entrada, y si es undef, ARG1 será el código de excepción.
Después de manejar la entrada, este manejador lleva de nuevo al manejador 'prompt', el cual reprograma el timeout y pide datos al usuario de nuevo.