Project Euler: Problema 3 con Clojure

Siguiendo con los problemas del Proyecto Euler, hoy toca el tercero:

¿Cuál es el mayor factor primo del número 600851475143?

Podríamos buscar alternativas sofisticadas, pero resolverlo por fuerza bruta es instantáneo:

(loop [n 600851475143 
       f 2]
  (cond
    (= n f) n
    (zero? (rem n f)) (recur (/ n f) f)
    :else (recur n (inc f))))

El algoritmo es de lo más simple, empezamos dividiendo el número entre 2 tantas veces como sea posible, luego entre 3, y así sucesivamente hasta que el número que nos queda y el factor por que queremos dividir sean iguales. Ese será el máximo factor primo.

La implementación tiene un par de cosas destacables para los que no sabemos muchos de Clojure:

La forma especial loop nos permite escribir bucles en Clojure. El primer argumento que recibe es un vector de bindings en el que enlazamos valores a variables, y el resto de argumentos son las instrucciones que forman parte del bucle. Para iterar, se utiliza la forma especial recur indicando los nuevos valores que deben tomar las variables declaradas en los bindings iniciales.

La forma especial recur tiene otro uso muy importante a la hora de definir funciones de recursión de cola, que son un caso especial de funciones de recursivas que por su forma pueden ser evaluadas de manera iterativa sin utilizar la pila.

La macro cond permite escribir algo parecido a un switch de C#, pero utilizando funciones como guardas de cada caso en lugar de tener que limitarse a valores constantes. Esto la convierte en una construcción mucho más versátil que un triste switch, a costa claro está de un peor rendimiento.

Y poco más que contar. Como era de esperar, para este tipo de problemas en los que hay que construir funciones puras, un lenguaje funcional proporciona una sintaxis muy tersa y limpia.