15 de abril de 2008

Tarjeta de programador

El fin de semana pasada me entro una inquietud. Saber como quedaría una tarjeta de presentación/visita de un programador. Y que ademas la tarjeta de presentación fuese un programa. Así que me he puesto a ello y aquí presento el resultado.

Empecemos con un par de reglas para hacer la tarjeta:


  1. Todo el texto debe poder compilarse/ejecutarse

  2. La información normal de una tarjeta de visita debe de quedar clara en el texto

  3. El texto tiene que ser suficientemente corto como para caber en una tarjeta


Yo aparte, en mi caso añadí la siguiente regla, tiene que ser en Haskell. Y esta es la primera versión que se me ocurrió:

data Career = Actor | Programmer | TaxiDriver | Writer
deriving( Show )
type Name = String
type Contact = String

careerOf :: Name -> Maybe Career
careerOf "Luis Jose Cabellos Gomez" = Just Programmer
careerOf _ = Nothing

contactOf :: Name -> Contact
contactOf "Luis Jose Cabellos Gomez" = "zhen.sydow at gmail.com"

main = putStrLn.unlines $ map
(\f-> f "Luis Jose Cabellos Gomez")
[ id, show.careerOf, contactOf ]

Lo que me gusta del código anterior es que la definición de las funciones por reglas hace que se lean casi como frases. También me gusta el chiste con el monad Maybe, Luis es "Just a Programmer", no me digáis que no queda bien. Lo que no me acaba de gustar es que las funciones son muy artificiales, y se repite en muchos sitios mi nombre. Y luego la función principal es muy confusa. Y para una definición de tipos tan triste, mejor nos quedamos con las macros de C.

Pasemos a la segunda versión.

data Career = Actor | Programmer | TaxiDriver | Writer
deriving Show

data Person = Person { name :: String
, career :: Maybe Career
, contact :: String }
deriving Show

me = Person "Luis Jose Cabellos Gomez"
(Just Programmer)
"zhen.sydow at gmail.com"

main = putStrLn $ show me

La segunda versión tiene lo bueno de la primera versión (no me podía quedar sin el chiste del Maybe) y tiene un par de mejoras. La función principal es muy sucinta, y la información principal no esta tan desperdigada. Ademas de tener una definición de tipos más completa. Me quedo con esta versión.

Cuando ya tenía el texto, pues me puse a decorarlo un poco. Se me ocurrió imprimirlo como su fuese una carta de poker. Usando el Inkscape me hice un símbolo lambda y lo puse como si fuese el palo de la baraja. A partir de ahora el poker se juega con corazones, diamantes, picas, tréboles y lambdas. Lo puse todo sobre una imagen, puse en negrita los datos . Y ya esta, ya tenia tarjeta de programador. A imprimirlo.


Y este es el resultado final. Tengo que decir que la impresora no lo imprimió todo lo bien que me gustaría. Y quizás con una fuente un poco más clara el resultado hubiese sido mejor, pero....


... me pregunto si será posible hacer algo tan elegante en código C++.

UPDATES:


FIN UPDATES

27 de febrero de 2008

Evaluación Perezosa

Hoy quisiera hablar de una de las características de Haskell más fascinantes, la evaluación perezosa. Y me han entrado ganas de hablar de ello al ver la utilización que se da a la evaluación perezosa en el problema repMin de Richard Bird. El problema viene a ser el siguiente: reemplazar los valores en los nodos de un árbol por el mínimo valor de este mismo árbol, y solo recorriendo el árbol una sola vez.

Yo voy a explicarlo con una estructura más sencilla, sustituir todos los valores de una lista de números por el mínimo valor de esta lista, recorriendo la lista una sola vez. Y esto usando de manera muy inteligente la evaluación perezosa. Vamos a empezar contando que es la evaluación perezosa (lazy).

Evaluación Perezosa

En los lenguajes de programación tradicionales, lo que tenemos es evaluación ansiosa (eager). Con este tipo de evaluación, cuando asignamos un valor a una variable, o pasamos un parámetro a una función, se calcula cual es el valor final a asignar o cual es el valor final del parámetro. Por ejemplo:

v = sqrt(2) + 3 * sqrt(5);

f( 4 / g( 7 ) );

Antes de asignar nada a v, se llamara a las funciones sqrt con sus parámetros correspondientes, luego se aplicara la expresión matemática para obtener el valor final a asignar a v. En el caso de la llamada a la función f, antes se ha de llamar a la función g y dividir entre 4 el resultado de esta llamada. Por eso se denomina evaluación perezosa, porque se ha de obtener el valor de las expresiones en cuanto el programa se las encuentra.

La evaluación perezosa solo calcula el valor de las expresiones cuando necesita hacer uso de este valor. En el código de ejemplo anterior, en v se guardaría como se calcula su valor y no el valor final. Y el parámetro pasado a f sería la formula completa y no el valor de aplicar la expresión. Si resulta que ni la variable v ni el parámetro de la función f se usan, pues nunca se llamaría a las funciones sqrt y g, ni se realizarían las operaciones matemáticas pertinentes.

Esto puede suponer una diferencia enorme entre ejecutar con evaluación perezosa y evaluación ansiosa. Si tenemos definidas f y g como:

int f( int a ){
return 4;
}
int g( int a ){
return a - 7;
}

Con evaluación ansiosa obtendríamos un error "division by zero" mientras que con evaluación perezosa la ejecución terminaría sin error.

ReplaceMin

La primera versión que podemos pensar de replaceMin es la que pongo a continuación:

replaceMin xs = replace xs (minimum xs)
where replace xs m = map (\a -> m) xs

Primero se calcula el mínimo de la lista de números xs y luego se sustituye todos los valores de esta lista por el valor mínimo (usando la función replace). Esta primera versión recorre la lista dos veces, una vez para calcular el mínimo y una segunda vez para reemplazar los valores.

Y ahora viene lo verdaderamente impresionante. La versión de replaceMin que recorre la lista una sola vez:

replaceMin xs = ys
where (ys, m) = rpMin xs m

¿Y como funciona? Gracias a la evaluación perezosa. Entendiendo lo que hace la función rpMin, lograremos entender como funciona. La función rpMin tiene dos parámetros, una lista de números xs y un numero m. Lo que hace rpMin es sustituir todos los valores de xs por m (igual que replace) con el añadido de que a la vez va calculando el mínimo de xs. La función rpMin devuelve una tupla con la nueva lista con los valores sustituidos y con el mínimo de la antigua lista.

La magia ocurre en la manera de llamar a rpMin desde la función replaceMin, como valor a sustituir se le pasa el mínimo que todavía no ha calculado rpMin y del cual no necesita saber su valor. Básicamente la conversación entre las dos funciones seria:
  • replaceMin: sustituyeme todos los valores de xs por este valor m del que de momento no se nada
  • rpMin: ¿Pero como? ¡Necesitare saber el valor de m para ponerlo en la lista!
  • replaceMin: ¡No hombre, no! Recuerda que nosotros tenemos evaluación perezosa.
  • rpMin: Bueno, pues tu sabrás cual es el valor de m, ya se lo dirás al que quiera usar la lista resultante.
  • replaceMin: ¡Te vuelves a equivocar! El valor me lo dirás tu, cuando calcules el mínimo de xs.

El valor de m solo será necesario fuera de la función replaceMin, por ejemplo cuando se imprima la lista resultante por pantalla, y para entonces rpMin ya habrá calculado el mínimo.

Esta sería la definición de rpMin. Simplemente va sustituyendo los valores de la lista por el valor m pasado como parámetro, y mientras va calculando el mínimo comparando cada valor de la lista con el mínimo hasta ese momento. Y todo recorriendo la lista una sola vez.

rpMin [x] m = ([m], x)
rpMin (x:xs) m = (m:ys, min x my)
where (ys, my) = rpMin xs m

Bueno, os dejo pensando en porqué funciona. O podeis probarlo por vosotros mismos en vuestra distribución de Haskell favorita. También os dejo los enlaces a las versiones de replaceMin para arboles.

Referencias:
Functional Pearl: Trees
Solving the repMin problem with I/O

27 de enero de 2008

Mi Moleskine


Hoy voy a hablar de mi Moleskine. Una moleskine es una libreta de notas. Pero no una libreta cualquiera. Tiene unas tapas duras de imitación de piel, una encuadernación impecable que por mucho que abras la libreta, no se te sueltan las hojas. Tiene una goma elástica para mantener cerrada la libreta, y un marcador de páginas. Ideal para llevar en el bolsillo o en la bandolera y no acabar con las todas las hojas.
Esta libreta de notas (no es solo un tipo de libreta, es una marca registrada) es famosa también por la gente que ha hecho uso de ella, desde artistas Europeos como Picasso y Van Gogh, hasta escritores actuales como Neil Gaiman. Y se entiende, la calidad de la libreta es impecable.
Yo uso un modelo pequeño de hojas lisas, que siempre llevo conmigo. Y en el apunto de todo:
  • ideas para programas que siempre se me ocurren cuando no tengo el ordenador a mano.
  • recomendaciones de libros y películas que me hace la gente.
  • Recetas de cocina.
  • notas de viaje, donde voy, donde me hospedo y a que hora sale el vuelo. Esto me ha sido de especial ayuda.
  • también uso la libreta para guardar facturas, tickets y tarjetas varias. Viene muy bien tener localizado todos estos papeles.
  • Y curiosidades varias, cosas que llaman mi atención y de las que deseo buscar información más adelante.
Pues nada, recomiendo a todo el mundo que se planteé comprar una libreta de notas moleskine. Las hay de diferentes tamaños y con diferentes tipos de hojas (desde agendas con una hoja por día, hasta libretas especial para ciudades, con su mapa callejero).

7 de enero de 2008

El vino, mejor cuando es libre

Haciendo analogía con el software libre, el vino también debería compartir los mismos derechos. Cuando compras una botella de vino deberías...

  • ... Poder beber el vino (libertad 0)
  • ... Poder mejorar el vino, echarle cocacola por ejemplo (libertad 1)
  • ... Poder compartir el vino con el de al lado (libertad 2)
  • ... Poder compartir la versión mejorada (libertad 3)
Puede parecer que el vino ya tiene todas la libertades posibles, pero cuando vamos a un restaurante, pedimos una botella y el camarero nos sirve, y nos rellena la copa cuando nos falta, claramente esta vulnerando las libertades 0 y 2. Aunque esto tiene fácil solución, basta con alejar la botella del camarero.

Dedicado a David que tuvo la idea de hacer más libre el vino (quizás por exceso o falta de vino :-)

7 de diciembre de 2007

Simplificar Funciones en Haskell

Hoy voy a hablar un poco acerca de un proyecto propio, y de como usar las características de un lenguaje como Haskell para escribir menos. El proyecto es una calculadora de pila (primero se escriben los operandos y luego se escribe el operador) y la podéis encontrar en Hscalc.

El código que viene a continuación son las funciones ejecutadas cuando se pulsa sobre los diferentes botones de la calculadora. Por ejemplo la función pulsaNumero inserta un nuevo dígito en la pila.

pulsaNumero v entries n = do
val <- readIORef v
let newVal = insertaDigito val n
writeIORef v newVal
putStackInEntries entries newVal

pulsaComa v entries = do
val <- readIORef v
let newVal = insertaComa val
writeIORef v newVal
putStackInEntries entries newVal

pulsaSigno v entries = do
val <- readIORef v
let newVal = insertaSigno val
writeIORef v newVal
putStackInEntries entries newVal

pulsaStackAdd v entries = do
val <- readIORef v
let newVal = nullValue:convertValues val
writeIORef v newVal
putStackInEntries entries newVal

pulsaStackClear v entries = do
writeIORef v pilaVacia
putStackInEntries entries pilaVacia

pulsaOpBinaria v entries f = do
val <- readIORef v
let newVal = aplicaFuncion val f
writeIORef v newVal
putStackInEntries entries newVal

Podemos ver que se repite un mismo patrón en todas las funciones. Primero se obtiene el valor actual de la calculadora, se aplica una función que modifica este valor y luego se guarda el nuevo valor y se actualiza la ventana. Con este patrón nos hacemos nuestra función base, la cual toma como primer parámetro la función a aplicar para modificar el estado de la calculadora. Esta función base es pulsaFuncion.

pulsaFuncion funcion v entries = do
-- cojer valor actual
val <- readIORef v
-- aplicar una modificacion al valor
let newVal = funcion val
-- actualizar valor
writeIORef v newVal
-- actualizar ventana
putStackInEntries entries newVal

La primera característica que vamos a aprovechar de Haskell es la aplicación parcial. Con esta característica, podemos definir nuevas funciones fijando el valor de parte de los parámetros. De esta manera definimos pulsaComa y pulsaFuncion, fijando el primer parámetro. Al resto de los parámetros se les da valor a la hora de invocar las nuevas funciones.

pulsaComa = pulsaFuncion insertaComa
pulsaSigno = pulsaFuncion insertaSigno

Para el resto de funciones tenemos que usar funciones anónimas. Una función anónima permite definir funciones en cualquier lugar en donde se pueda usar una expresión. En Haskell, las funciones anónimas se definen de la siguiente manera.

-- función anónima con dos parámetros a y b
(\a b-> a+b)

Usando una función anónima definimos pulsaStackAdd con una función que dado un valor, le inserta un cero al principio.

pulsaStackAdd =
pulsaFuncion (\v-> nullValue:convertValues v)

Para la función pulsaStackClear, ignoramos el parámetro y siempre devolvemos una pila vacía. Aunque el código original de pulsaStackClear era menor, el resultado es el mismo al ser Haskell un programa de evaluación perezosa. No realiza la llamada para obtener el valor actual de la calculadora ya que luego no va a usar ese valor.

pulsaStackClear = pulsaFuncion (\_-> pilaVacia)

Para las funciones pulsaNumero y pulsaOpBinaria necesitamos un parámetro adicional, el resto es igual que en los casos anteriores.

pulsaNumero n = pulsaFuncion (\v-> insertaDigito v n)
pulsaOpBinaria f = pulsaFuncion (\v-> aplicaFuncion v f)

Con estos cambios el código se simplifica enormemente, aumentando en legibilidad y siendo mucho más fácil de mantener. Si queremos hacer algo adicional, con cambiar la función base nos bastaría.

pulsaFuncion funcion v entries = do
val <- readIORef v
let newVal = funcion val
writeIORef v newVal
putStackInEntries entries newVal

pulsaComa = pulsaFuncion insertaComa

pulsaSigno = pulsaFuncion insertaSigno

pulsaStackAdd =
pulsaFuncion (\v-> nullValue:convertValues v)

pulsaStackClear = pulsaFuncion (\_-> pilaVacia)

pulsaNumero n = pulsaFuncion (\v-> insertaDigito v n)

pulsaOpBinaria f = pulsaFuncion (\v-> aplicaFuncion v f)