Ésta es una forma de hacerlo que le da un poco de la exposición a las secuencias perezosos, aunque ciertamente no es realmente una manera óptima de cálculo de la sucesión de Fibonacci.
Dada la definición de la secuencia de Fibonacci, podemos ver que se ha creado aplicando repetidamente la misma regla al caso base de '(1 1)
. La función Clojure iterate
suena como que sería bueno para esto:
user> (doc iterate)
-------------------------
clojure.core/iterate
([f x])
Returns a lazy sequence of x, (f x), (f (f x)) etc. f must be free of side-effects
Así que para nuestra función nos gustaría algo que toma los valores que hemos computa hasta ahora, suma los dos más recientes, y devuelve una lista del nuevo valor y todos los valores anteriores.
(fn [[x y & _ :as all]] (cons (+ x y) all))
La lista de argumentos aquí sólo significa que x
y y
estará atado a los dos primeros valores de la lista se pasa como argumento de la función, una lista que contiene todos los argumentos después de los dos primeros estará obligado a _
, y la lista original aprobada como argumento para la función se puede consultar a través del all
.
Ahora, iterate
devolverá una secuencia infinita de valores intermedios, por lo que para nuestro caso, querremos envolverlo en algo que simplemente devuelva el valor que nos interesa; la evaluación diferida detendrá la secuencia infinita completa que se evalúa.
(defn fib [n]
(nth (iterate (fn [[x y & _ :as all]] (cons (+ x y) all)) '(1 1)) (- n 2)))
Tenga en cuenta también que esto devuelve el resultado en el orden opuesto a su implementación; es una cuestión simple arreglar esto con reverse
, por supuesto.
Editar: o, de hecho, como dice amalloy, mediante el uso de vectores:
(defn fib [n]
(nth (iterate (fn [all]
(conj all (->> all (take-last 2) (apply +)))) [1 1])
(- n 2)))
Más respuestas: http://codereview.stackexchange.com/questions/300/project-euler-problem-2-in-clojure – Jeremy