viernes, diciembre 14, 2007

Mandelbrot en F#

Una vez un profesor dijo lo siguiente. "Para aprender un lenguaje (de programación) lo primero es entender su sintaxis para lo cual basta con pegarse a leer la especificación sintáctica en BNF, el resto es conocer las API's". En cierta forma estoy de acuerdo. Sin importar que tan bueno se sea jugando con la sintaxis de un lenguaje o las posibilidades de estilo de programación que permite, aprender un lenguaje es en cierta forma conocer sus API's.

Como reto a corto plazo quiero hacer un pequeño Ray-Tracer en F#. Alguna vez hice uno en Java no fue muy difícil. Para esto hay básicamente 2 componentes

1. La parte que coja las librerías de dibujo y a partir de un bitmap pinte la imagen
2. Más importante aún, el algorítmo de Ray-Tracing

Nunca en mi vida había utilizado librerías de Microsoft. Nunca en mi vida había utilizado MSDN y puedo decir que resulta relativamente fácil. Entonces, para superar la primera parte decidí hacer un pequeño reto que pintara sobre un bitmap. Pero que iba a pintar?

Me acordé que cuando hice el Ray-Tracer en Java, saque la parte que pintaba en un bitmap de un ejemplo que pintaba el bien conocido Mandelbrot. Entonces decidí empezar por ésto. Intentar pintar el Mandelbrot.

Busca uno en wikipedia y se encuentra el algoritmito

y listo... Primer problema. él algoritmo como suele suceder es imperativo. Pues bien, F# permite código imperativo y algún día tenía que aprender.

Entonces...

let mandelbrot (x,y) =
let x0 = ref x in let y0 = ref y
let i = ref 0
while (!x0 * !x0 + !y0 * !y0 < 4.0) && (!i < 1000) do
let tmpx = !x0 * !x0 - !y0 * !y0 + x
let tmpy = 2.0 * !x0 * !y0 + y
x0 := tmpx
y0 := tmpy
i := !i + 1
if !i = 1000 then 0.0 else 1.0

Algunas generalidades sobre él código.
Para alguien cuya experiencia con código funcional haya implicado únicamente Haskell él código debe resultar espantoso. Por? Simplemente por el while. Para generar éste while se hace uso de objetos mutables (ref i, ref x0 y ref y0). A diferencia de Haskell, F# permite utilizar objetos con estado. estas ref, son casi como apuntadores. Hacen referencia a partes en memoria, lo cual implica side effects y todas las cosas "maravillosas" del mundo imperativo.

En fin... después de tener ésto implementado (Junto con la parte de System.Drawing) se genera la siguiente imagen:



En este momento la imagen es binaria. si el punto pertenece al conjunto se pinta, sino no. Nada impresionante. Una forma de hacerlo más bonito es asignando un color dependiendo de la cercanía al conjunto. Cuestión de devolver un valor no binario que se tendrá en cuenta a la hora de pintar. Yo lo hice de la siguiente manera. Reemplacé en el código anterior la última línea por ésta:


1.0/(Float.of_int (!i + 1))


El resultado:



Todo el código fuente está aquí.

No hay comentarios.: