No es recursivo todo lo que parece.

En la entrada anterior hablé sobre la recursividad mediante Scheme y la función de Ackermann. Hay algo curioso, o al menos a mi me sorprende, sobre Scheme y es como interpreta como iterativos algoritmos aparentemente recursivos. Como ejemplo 2 algoritmos para calcular los números de Fibonacci, uno recursivo y el otro iterativo aunque parezca recursivo. Primero recordar que estos números de definen así:

0 Si n = 0

1 Si n = 1

Fib(n – 1) + Fib(n – 2) Si n ≠ 0 y n ≠ 1

Esta definición lleva directamente a este algoritmo recursivo en Scheme:

(define (fib n)
    (cond ((= n 0) 0)
    ((= n 1) 1)
    (else (+ (fib (- n 1))
        (fib (- n 2))))))

Funciona pero es poco eficiente, la complejidad aumenta exponencialmente a medida que el número que buscamos, n, es mayor, pues se trata de recursividad en árbol. Este otro algoritmo es mucho más eficiente:

(define (fib n)
    (fib-iter 1 0 n))

(define (fib-iter a b count)
    (if (= count 0)
    b
    (fib-iter (+ a b) a (- count 1))))

Si queremos saber el Fibonacci para 4 lo invocamos así:

(fib 4)

Y hará:

(fib 4)

(fib-iter 1 0 4)

(fib-iter 1 1 3)

(fib-iter 2 1 2)

(fib-iter 3 2 1)

(fib-iter 5 3 0)

3

Scheme lo ejecuta como iterativo, no como recursivo. ¿Pero que sucede si implementamos ese mismo algoritmo en un lenguaje tipo procedimental como C o Pascal? Veamos como podría ser en PHP, el rey de la web:

function fib ($a, $b, $aCalc) {

    if ($aCalc == 0) {
        return $b;
    }else{
        $aCalc--;
        fib($a + $b, $a, $aCalc);
    }
}

$result = fib (1, 0, 4);
echo $result;

Y no funcionará. ¿Por qué? Pues porque PHP lo interpretará como recursivo, no como iterativo, y hará:

(fib 1 0 4)

(fib 1 0 4)(fib 1 1 3)

(fib 1 0 4)(fib 1 1 3)(fib 2 1 2)

(fib 1 0 4)(fib 1 1 3)(fib 2 1 2)(fib 3 2 1)

(fib 1 0 4)(fib 1 1 3)(fib 2 1 2)(fib 3 2 1)(fib 5 3 0)

Pero en vez de quedarse aquí empezará a hacer los «returns», los «pops» de la pila:

(fib 1 0 4)(fib 1 1 3)(fib 2 1 2)(fib 3 2 0)

(fib 1 0 4)(fib 1 1 3)(fib 2 1 1)

(fib 1 0 4)(fib 1 1 2)

(fib 1 0 3)

:-/

Entonces, ¿como hacer «entender» al compilador o intérprete en este tipo de lenguajes que deseamos una implementación iterativa? La única opción es hacer servir las instrucciones que disponen para bucles: while, for, repeat, etc. Este podría ser un ejemplo en PHP:

function fibonacci($n) {
    $f[0] = 0;
    $f[1] = 1;

    for ($i = 2; $i <= $n; $i++) {
        $f[$i] = $f[$i-1] + $f[$i-2];
    }

    echo $f[$n];
}
fibonacci(8);

Ésta curiosidad se puede expresar también a la inversa: Scheme interpreta como iterativos algoritmos que a quienes estamos acostumbrados a los lenguajes por preocedimientos (procedural languages) nos parecen iterativos. Probablemente ésta es mejor forma de expresarlo.

La función de Ackermann

Llevo un par de meses bastante entretenido con el libro del MIT «Structure and Interpretation of Computer Programs». Si bien el MIT nos permite el acceso gratuitamente a todo su contenido aquí, es un libro genial que realmente vale la pena comprárselo.

En uno de sus primeros capítulos, donde se contrastan los procesos iterativos con los recursivos y como aquel que no quiere la cosa, en un ejercicio aparentemente inocente aparece un procedimiento que computa una variante de la función matemática de Ackermann:

(define (A x y)
    (cond ((= y 0) 0)
    ((= x 0) (* 2 y))
    ((= y 1) 2)
    (else (A (- x 1)
        (A x (- y 1))))))

Dicha función es famosa en teoría de la computación. Lo que sorprende de dicha función, a diferencia de las que habitualmente se usan como función «modelo» para enseñar qué es la recursividad, es que no es recursiva primitiva. El ejemplo típico de la función recursiva es la que nos da el factorial de un número n:

(define (factorial n)
    (if (= n 1)
    1
    (* n (factorial (- n 1)))))

El proceso que seguiría para calcular por ejemplo el factorial de 6 sería:

factorial(6)

6 * factorial(5)

6 * (5 * factorial(4))

6 * (5 * (4 * factorial(3)))

6 * (5 * (4 * (3 * factorial(2))))

6 * (5 * (4 * (3 * (2 * factorial(1)))))

Este es el punto en que más se carga la pila, a partir de aquí serían «pops»:

6 * (5 * (4 * (3 * (2 * 1))))

6 * (5 * (4 * (3 * 2)))

6 * (5 * (4 * 6))

6 * (5 * (24))

6 * (120)

720

Las funciones recursivas «típicas» siguen éste patrón, se van sumergiendo hasta llegar a un punto de inflexión a partir del cual van emergiendo hasta aflorar el resultado final. La función de Ackermann podría parecer que transcurre igual, por ejemplo (A 1 6) sería:

(A 1 6)

(A 0 (A 1 5))

(A 0 (A 0 (A 1 4)))

(A 0 (A 0 (A 0 (A 1 3))))

(A 0 (A 0 (A 0 (A 0 (A 1 2)))))

(A 0 (A 0 (A 0 (A 0 (A 0 (A 1 1))))))

(A 0 (A 0 (A 0 (A 0 (A 0 2)))))

(A 0 (A 0 (A 0 (A 0 4))))

(A 0 (A 0 (A 0 8)))

(A 0 (A 0 16))

(A 0  32)

64

Es decir, 26 De hecho, para todo (A 1 n) el resultado siempre será 2n Pero veamos por ejemplo como transcurre al calcular (A 2 4):

(A 2 4)

(A 1 (A 2 3))

(A 1 (A 1 (A 2 2)))

(A 1 (A 1 (A 1 (A 2 1))))

(A 1 (A 1 (A 1 2)))

(A 1 (A 1 (A 0 (A 1 1))))

(A 1 (A 1 (A 0 2)))

(A 1 (A 1 4))

(A 1 (A 0 (A 1 3)))

Como se puede observar, se va profundizando para después emerger y a continuación volver a sumergirse. Después de un largo proceso recursivo, el resultado final que acaba dando es 65536, es decir, 216.

IDE, Vim o Emacs

¿Qué editor usar para programar? Es un tema que despierta pasiones, sólo las discusiones Emacs vs Vim ya se encienden con usuarios de estos editores, que en algunos casos llegan a llevar incluso décadas usándolos, pero no es sobre esto sobre lo que quiero opinar aquí sino sobre si usar uno de estos clásicos editores o un «moderno» IDE como Eclipse o NetBeans (mi favorito). Aquí también alguien podrá objetar que tanto Vim mediante plugins y sobretodo Emacs son auténticos IDEs, para quienes piensen así que por favor interpreten que el debate es entre viejos y nuevos IDEs.

En mi opinión, IDEs como NetBean o Eclipse sirven para trabajar en EL PROYECTO, ese proyecto que se va desarrollando durante meses o años y que puede que tenga a otros desarrolladores involucrados. Mientras que prefiero usar editores tipo Vim o Emacs en esos proyectos más pequeños y diversos. Hasta hace poco creía que un editor como Nano era suficiente para trabajar en el servidor con los ficheros de configuración o para arreglar algún marrón en vivo pero ahora veo que es rentable molestarse en aprender lo básico de una herramienta más sofisticada como Vim o Emacs.

Y si me preguntan si Vim o Emacs respondo que Vim. Emacs es un monstruo que hace parecer a Unix un plugin suyo 🙂 Basta decir que su lenguaje para ampliarle funcionalidades es Emacs Lisp, un dialecto de Lisp.

Creear una comunidad sobre tecnología en Cali

Me gustaría crear una comunidad de desarrolladores, administradores de sistemas y de todo tipo de personas interesadas en las nuevas tecnologías aquí en Cali. Una comunidad que se pueda reunir periódicamente para tratar y desarrollar los temas que más nos plazcan, compartir conocimientos… una especie de hacklab donde colaborar y socializar. Más detalles como dónde nos reuniríamos, periodicidad y demás concretémoslos cuando seamos algunos. Si estás interesad@ en llevar acabo esta idea puedes ponerte en contacto conmigo a través de la página de contacto o bien puedes dejar aquí mismo un comentario.

¡Espero que la idea guste y podamos llegar a ser unos cuantos! Si conoces a alguien que crees pudiera estar interesad@ por favor difunde este mensaje, ¡gracias!

— Editado el 01/02/2012 —

Esta propuesta me ha llevado a conocer al grupo caleño DELM ¡Espero que nos podamos reunir próximamente!

— Editado el 04/02/2012 —

En espera de la próxima reunión de DELM los interesados en el proyecto del que hablo aquí nos reunimos mañana domingo a las 10 de la manaña en el centro comercial Chipichape.

— Editado el 24/03/2012 —

Estuvo muy interesante la reunión y estoy esperando la próxima. No obstante, aunque pude conocer interesantes personas y proyectos, encontré a faltar más personas interesadas en el aspecto ingenieril para poder formar una comunidad más similar a la aquí descrita. Interesad@s ya sabéis, poneros por favor en contacto conmigo.

Tanto esfuerzo en escolarizarnos para después no aprovecharlo

escolarización

Recuerdo cuando estudiaba Bachillerato, que en el libro de historia se citaba un texto escrito por un tipo español en el siglo XIX en que alertaba de los «peligros» de alfabetizar a las clases populares; según él sólo servia para que estos acabaran leyendo panfletos anarquistas y comunistas y, en consecuencia, aumentara el crispamiento en la sociedad. Hoy en día nos parece muy incorrecto que este señor opinase que a las clases trabajadoras era mejor no alfabetizarlas, e inicialmente parece que las ideas elitistas de este tipo han acabado donde se merecen: en el basurero de la historia. Ahora bien, algunas veces pienso que esto no es tan evidente. Si bien es cierto que los países desarrollados tienen toneladas de bachilleres y universitarios, y muchos de ellos proceden de la clase obrera, a veces me pregunto en qué han quedado las enseñanzas que hemos recibido y supuestamente asimilado y, sobretodo, dónde ha quedado el espíritu crítico que se supone que alguien con cierta cultura debería tener. A continuación voy a exponer a modo de ejemplo algunos de los casos que me provocan tales pensamientos. Sigue leyendo

Ocultar la versión de Apache y PHP en las cabeceras

Depende de cómo esté configurado Apache puede estar proporcionando a cualquier curioso con intenciones poco agradables información sobre la versión que se está usando de PHP y de Apache, sabiendo la versión del programa se pueden encontrar con gran facilidad exploits en Internet para vulnerabilidades conocidas. Aquí pueden ver las cabeceras de un Apache que habla demasiado:

Cabeceras con demasiada información

Para que Apache oculte su versión existe la directiva ServerTokens desde la versión 2.0.44 Aquí están los resultados que dan sus opciones:

ServerTokens Prod[uctOnly] => Server: Apache
ServerTokens Major => Server: Apache/2
ServerTokens Minor => Server: Apache/2.0
ServerTokens Min[imal] => Server: Apache/2.0.41
ServerTokens OS => Server: Apache/2.0.41 (Unix)
ServerTokens Full => Server: Apache/2.0.41 (Unix) PHP/4.2.2 MyMod/1.2

Como podemos ver, ServerTokens Prod es la que más nos conviene pues es la que da menos información. Desde la versión 2.0.44 está directiva controla la directiva ServerSignature. Para una versión anterior deberíamos usar ServerSignature Off además de ServerTokens.

En la imagen también podemos ver un «X-Powered-By» que proporciona la versión de PHP, lo cual tampoco es nada conveniente. Para evitarlo en el fichero de configuración de PHP php.ini debemos poner a Off la directiva «expose_php».

Resumiendo, en httpd.conf debemos tener:

ServerTokens Prod

# Opcionalmente:
ServerSignature Off

Y en php.ini:

expose_php = Off

Así obtendremos unas cabeceras más discretas:

Cabeceras sin excesiva información

INFORMACIÓN IMPORTANTE: esto para nada substituye la necesidad de mantener nuestro software actualizado. Tan sólo evita hacerle la vida demasiado fácil a un atacante. Debemos tener en cuenta que mediante herramientas como los scanners de «seguridad» como nmap es posible saber la versión de los servicios que corre un servidor. Por no hablar de worms que van probando servidores y, a veces sin ni tan siquiera haber comprobado previamente la versión, van lanzando exploits en busca de una vulnerabilidad explotable.