Ecco un modo di farlo che ti dà un po 'di esposizione a sequenze pigri, anche se non è certamente in realtà un modo ottimale di calcolare la sequenza di Fibonacci.
Data la definizione della sequenza di Fibonacci, possiamo vedere che è costruita applicando ripetutamente la stessa regola al caso base di '(1 1)
. La funzione Clojure iterate
suona come sarebbe bene per questo:
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
Quindi per la nostra funzione vorremmo qualcosa che richiede i valori che abbiamo calcolata finora, riassume i due più recenti, e restituisce una lista del nuovo valore e tutti i vecchi valori.
(fn [[x y & _ :as all]] (cons (+ x y) all))
L'elenco degli argomenti qui significa semplicemente che x
e y
saranno vincolati ai primi due valori della lista passata come argomento della funzione, una lista contenente tutti gli argomenti dopo i primi due verrà associato al _
, e l'elenco originale passato come argomento alla funzione può essere riferito a all
.
Ora, iterate
restituirà una sequenza infinita di valori intermedi, quindi per il nostro caso vorremmo racchiuderlo in qualcosa che restituisca solo il valore a cui siamo interessati; la valutazione lazy interromperà l'intera sequenza infinita valutata.
(defn fib [n]
(nth (iterate (fn [[x y & _ :as all]] (cons (+ x y) all)) '(1 1)) (- n 2)))
Si noti inoltre che questo restituisce il risultato nell'ordine opposto all'implementazione; è semplice risolvere il problema con reverse
ovviamente.
Edit: o addirittura, come amalloy dice, utilizzando vettori:
(defn fib [n]
(nth (iterate (fn [all]
(conj all (->> all (take-last 2) (apply +)))) [1 1])
(- n 2)))
più risposte: http://codereview.stackexchange.com/questions/300/project-euler-problem-2-in-clojure – Jeremy