Memoization: Acelerando funciones recursivas. Ejemplo con Javascript.

En su día hablamos aquí sobre la recursividad y sus “peligros“. Las funciones recursivas son muy costosas a nivel de recursos pues al llamarse a si mismas repiten varias veces el mismo paso ¿hay una solución?

Memoization (memorización) es una técnica consistente en almacenar los resultados de funciones muy costosas para devolverlos directamente en lugar de volver a calcularlos.

Por ejemplo, la siguiente función que ya vimos en un artículo anterior nos permitiría calcular un factorial con javascript:

function factorial(num)
{
    if (num < 0) {
        return 0;
    }
    else if (num == 0) {
        return 1;
    }
    else {
        return (num * factorial(num - 1));
    }
}

¿Podemos optimizar mucho esta función cacheando los datos? Podemos:

function factorial(num) {
    // inicializamos si es necesario
    if (!factorial.cache) {
        factorial.cache = {};
    }
    if (num == 0) {
        return 1;
    } else if (num in factorial.cache) {
    // si ya tenemos en cache el factorial de num, lo devolvemos
        return factorial.cache[num];
    } else {
    factorial.cache[num] = num * factorial(num - 1);
    return factorial.cache[num];
}
 

De esta forma nos ahorramos varias llamadas a la función pues simplemente devolvemos un valor almacenado en memoria. Esta no es una técnica exclusiva de Javascript, podemos utilizarla con otros lenguajes de programación. Javascript sí tiene la particularidad de tratar las funciones como un objeto más, eso nos permite por ejemplo definirle la propiedad cache en el ejemplo de arriba (en otros lenguajes deberíamos pasarle como parámetro a la función el array de valores o declararlo como variable global), esto también nos permite hacer que una función reciba como parámetro una función o devuelva como resultado otra función, lo que se conoce como higher-order function o función de orden superior. Sirviéndonos de esto podríamos declarar una función genérica para nuestro memoization:

const memoization = function(func){
    const cache = {};
    return (...args) => {
        const key = [...args].toString();
        return key in cache ? cache[key] : (cache[key] = func(...args));
    }
}

De esta forma podríamos definir nuestra función factorial() tal que así:

var factorial = memoization(function(num) {		
    return (num <= 1) ? 1 : num * factorial(num-1);
})

Anuncios

Añadir un PDF a un sitio web usando una etiqueta de HTML5

Existen varias formas de insertar un PDF dentro de un sitio web, una de ellas es usar alguna de estas etiquetas:

  • embed
  • object
  • iframe

Antaño solo teníamos la opción de usar la etiqueta iframe, dado que embed y object se añadieron para la especificación HTML5. En todo caso el procedimiento con las tres es el mismo: ponemos la URL del fichero PDF como valor del atributo src y definimos el tamaño mediante los atributos width y height:

<!--Ejemplos varios--&gt;
<iframe src="http://rutaamipdf/fichero.pdf" width="95%" height="100%"&gt;</iframe&gt;

<embed src="http://rutaamipdf/fichero.pdf" width="95%" height="100%"&gt;</embed&gt;

<object src="http://rutaamipdf/fichero.pdf" width="95%" height="100%"&gt;</object&gt;

¿Problemas? Pues sí, que con este método dependemos de que el navegador que está usando el usuario tenga incorporado algún visualizador de PDF. En algunos casos tiene que ser mediante un plugin externo, como en Chrome, en otros ya viene incluído por defecto en el propio navegador.

Cinco consejos a la hora de hacer scripts de Bash en Linux.

Llevamos unos días donde he hablado mucho de PowerShell y parece que nos estemos olvidando un poco del mundo Linuxero, así que hoy vamos a ver una serie de consejillos para hacer scripts con Bash en nuestro sistema Linux de forma más efectiva:

Usa comillas dobles a la hora de referenciar variables:

Trabajar con cadenas de texto con espacios puede ser problemático. A la hora de referenciar una variable para usar su valor es recomendable hacerlo poniéndola entre comillas dobles: evitará que la cadena se corte por el uso de espacios en blanco y también mejorará el rendimiento, evitando hacer rastreos de comodines innecesariamente. Si ejecutas este script lo verás:

empresas="Tecnológicas Camarasa SL"
echo "Sin comillas:"
echo
for nombre in $empresas; do
        echo "$nombre"
done
echo
echo "Con comillas:"
echo
for nombre in "$empresas"; do
        echo "$nombre"
done
exit 0

Si lo ejecutas verás como en el primer caso va a imprimir tres nombres, pues se confundirá con los espacios, mientras que en el ejemplo con comillas lo sacará en una sola línea respentado los espacios.

Forzar la salida del script en caso de error:

A veces un script puede seguir ejecutándose aunque un comando falle, y eso puede derivar en problemas más adelante. Con la siguiente línea podemos forzar la salida del script en caso de que un comando no funcione:

set -o errexit 

Forzar la salida del script en caso de usar una variable no declarada:

Como en el caso anterior, esto puede derivar en errores más graves más adelante, así que es recomendable que el script se detenga si se usa una variable que está sin declarar:

set -o nounset

Declara las constantes como readonly:

Las constantes en esencia son un variable con un valor estático. A la hora de declarar una constante en nuestro script, tanto por seguridad como por rendimiento, lo mejor es hacerlo con el atributo readonly, puesto que no va a vaciarse ni sobrescribirse el valor.

readonly fichero_host="/etc/hosts"
readonly pi=3.14159265359

Usa la sintaxis $(comando) en lugar del antiguo `comando` :

Si queremos asignar la salida de un comando a una variable antiguamente lo haríamos invocando al comando entre dos acentos. Hoy por hoy la sintaxis sería utilizando el comando entre paréntesis precedido por el carácter $, tal que así:

#Sintaxis antigua
usuario=`echo “$UID”`
#Sintaxis modernizada recomendada
usuario=$(echo “$UID”)

Emular el funcionamiento de un operador ternario ?: en ASP clásico con VBScript

Hemos hablado sobre el operador ternario ?: en Java o como emularlo en Python. Pero ¿Existe en el viejo VBScript? Pues no. ¿Qué soluciones tenemos?

Realmente cuando trabajamos en ASP Clásico el VBScript nos permite hacer un if-else en una sola línea tal que así:

If diaDeLaSemana < 6 Then seTrabaja = True Else seTrabaja = false

Por otra parte podríamos crear una función que emule su funcionamiento. Recibiría como parámetros una expresión que nos devuelva un valor booleano, una cadena que devolver en caso de que la expresión sea cierta y otra para el caso de que sea falsa:

Function IfT(bExpr, rVerdadera, rFalsa)
    If CBool(bExpr) Then
        IfT = rVerdadera
    Else 
        IfT = rFalsa
    End If
End Function

Veamos un ejemplo:

HoyQueHago = IfT(diaDeLaSemana < 6, "Currar", "Descansar")

Si el día de la semana es menor que seis (es decir, de lunes a viernes) devolvería “Currar” y si es mayor o igual (sábado o domingo) devolvería “Descansar“.

Javascript: funciones querySelector() y querySelectorAll()

Las funciones querySelector() y querySelectorAll() existen en Javascript como métodos del objeto document. Ambas nos permiten acceder a elementos del DOM utilizando un selector de CSS.

La función querySelector() nos devolverá el primer elemento que coincida con el selector que le pasemos. Si es un id no habría problema puesto que deberían ser únicos en el documento, pero si se tratase de una clase o un elemento HTML entonces nos devolvería solo la primera ocurrencia:

//Si es un id nos devolverá ese elemento
var i = document.querySelector('#Contenedor');

//pero si es un elemento que existe varias veces
//entonces solo tendremos la primera ocurrencia
var x = document.querySelector('p');

//esto pondría el fondo gris al primer p que haya 
//en nuestro documento
x.style.backgroundColor = "#d9d9d9";

¿Y si queremos todos los elementos?

En ese caso tenemos que usar querySelectorAll(). El funcionamiento es igual que el de querySelector(), pero en este caso nos devolverá un objeto Nodelist que contiene todos los elementos que coincidan con el selector que hemos buscado. En este caso no tendría sentido usarlo con id, ya que debería ser un elemento único.

//Aquí tendríamos una lista estática con todos los elementos p
var x = document.querySelector('p');

¿Y cómo accedemos a los elementos del Nodelist?

Al ser una lista podemos acceder a los elementos contenidos en el Nodelist que nos devuele querySelectorAll() mediante un índice. Podemos conocer el tamaño de la lista accediendo a la propiedad length e iterar todos los elementos dentro de un bucle:

//Aquí tendríamos una lista estática con todos los elementos p
var x = document.querySelector('p');

//ahora los recorremos y, para el ejemplo, cambiaremos el color
//de fondo por un gris
var i;
for (i = 0; i < x.length; i++) {
  x[i].style.backgroundColor = "#d9d9d9";
}

jQuery: Poner el foco en el siguiente elemento usando una clase

Os cuento: un cliente pide que en un formulario que está rellenando, con varios campos de texto muy pequeños para un solo carácter, el foco salte de forma automática al siguiente campo cada vez que cubre uno. Es decir, en cada evento de levantar la tecla salta al siguiente. La cosa parecía simple usando la función next() de jQuery:

$(".cuadrito").keyup(function() {
  $(this).next('.cuadrito').focus();	
});

La función next() de jQuery salta al siguiente elemento “hermano” que aparezca. ¿Qué quiere decir hermano? Pues que tengan el mismo padre, esto es que estén contenidos en el mismo elemento del DOM. Y por eso esta práctica y elegante solución no me valía, porque resulta que los inputs están dentro de una tabla, de forma que al cambiar de celda cambia el “padre” en el DOM y ya no salta el foco. ¿Qué hacer entonces? Lo solucioné con la función index():

$(".cuadrito").keyup(function() {	            
  var ind = $(".cuadrito").index(this);
  $(".cuadrito").eq(ind + 1).focus();
});

¿Qué hicimos aquí? Creo que en el pasado ya vimos que jQuery nos permite tratar a los elementos como si de un array se tratase. Simplemente estoy mirando en la primera fila qué índice tendría el elemento en el que nos encontramos y, en la siguiente, poniendo el foco en el posterior sumando 1 al índice.

Utilizar el comando expr para hacer operaciones

El comando expr del terminal de Linux nos permite evaluar una expresión y pintar su resultado. De esta forma podemos usarlo para ejecutar operaciones aritméticas o de comparación.

$ expr 15 + 3
$ expr 15 % 3
$ expr 15 \* 3
$ expr 15 – 3
$ expr 15 / 3

En el ejemplo de arriba puedes ver los ejemplos para suma, operación de módulo, multiplicación (ese caso requiere usar el carácter \ para escapar el asterisco que usamos como símbolo de multiplicación), resta y división.

¿Limitaciones? Pues que solo nos permite operar con números enteros: únicamente acepta enteros como parámetros y tan solo devuelve enteros como resultado

Otra cosa que nos permite expr es ejecutar operaciones de comparación:

$ expr 15 = 3
$ expr 15 != 5
$ expr 15 \> 3
$ expr 15 \< 3
$ expr 15 \<= 3

Devolverá 1 en caso de que la comparación sea verdadera y 0 en el caso contrario. De nuevo el carácter \ será necesario para escapar los caracteres de mayor y menor en las comparaciones.

No solo podemos usarlo desde el terminal para operar, expr también puede resultar de utilidad invocado dentro de alguno de nuestros scripts.