una función recursiva de Fibonacci en Clojure


Soy un recién llegado a Clojure que quería ver de qué se trata todo el alboroto. Calculando que la mejor manera de tener una idea de ello es escribir un código simple, pensé que comenzaría con una función de Fibonacci.

Mi primer esfuerzo fue:

(defn fib [x, n]
  (if (< (count x) n) 
    (fib (conj x (+ (last x) (nth x (- (count x) 2)))) n)
    x))

Para usar esto necesito sembrar x con [0 1] cuando llame a la función. Mi pregunta es, sin envolverlo en una función separada, es posible escribir una sola función que sólo toma el número de elementos a devolver?

Haciendo algunos leer alrededor me llevó a algunas formas mejores de lograr la misma funcionalidad:

(defn fib2 [n]
  (loop [ x [0 1]] 
    (if (< (count x) n) 
      (recur (conj x (+ (last x) (nth x (- (count x) 2)))))
      x)))

Y

(defn fib3 [n] 
  (take n 
    (map first (iterate (fn [[a b]] [b (+ a b)]) [0 1]))))

De todos modos, más por el bien del ejercicio que cualquier otra cosa, ¿puede alguien ayudarme con una mejor versión de una función Fibonacci puramente recursiva? ¿O quizás compartir una función mejor/diferente?

Author: richc, 2012-01-20

8 answers

Para responder a su primera pregunta:

(defn fib
  ([n]
     (fib [0 1] n))
  ([x, n]
     (if (< (count x) n) 
       (fib (conj x (+ (last x) (nth x (- (count x) 2)))) n)
       x)))

Este tipo de definición de función se denomina definición de función multiaritaria. Puede obtener más información aquí: http://clojure.org/functional_programming

En cuanto a una mejor función Fib, creo que su función fib3 es bastante impresionante y muestra una gran cantidad de conceptos de programación funcional.

 16
Author: vedang,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-01-20 10:41:58

En Clojure es realmente recomendable evitar la recursión y en su lugar usar las formas especiales loop y recur. Esto convierte lo que parece un proceso recursivo en uno iterativo, evitando desbordamientos de pila y mejorando el rendimiento.

Aquí hay un ejemplo de cómo implementaría una secuencia de Fibonacci con esta técnica:

(defn fib [n]
  (loop [fib-nums [0 1]]
    (if (>= (count fib-nums) n)
      (subvec fib-nums 0 n)
      (let [[n1 n2] (reverse fib-nums)]
        (recur (conj fib-nums (+ n1 n2)))))))

La construcción loop toma una serie de enlaces, que proporcionan valores iniciales, y una o más formas de cuerpo. En cualquiera de estas formas corporales, una llamada a recur hará que el bucle se llame recursivamente con los argumentos proporcionados.

 6
Author: rpowell,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-01-12 08:46:46

Esto es rápido y fresco:

(def fib (lazy-cat [0 1] (map + fib (rest fib))))

Desde: http://squirrel.pl/blog/2010/07/26/corecursion-in-clojure /

 6
Author: nickik,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-03-04 14:14:53

Puedes usar el operador thrush para limpiar #3 un poco (dependiendo de a quién le preguntes; algunas personas aman este estilo, otras lo odian; solo estoy señalando que es una opción):

(defn fib [n] 
  (->> [0 1] 
    (iterate (fn [[a b]] [b (+ a b)]))
    (map first)
    (take n)))

Dicho esto, probablemente extraería la (take n) y solo tendría la función fib ser una secuencia infinita perezosa.

(def fib
  (->> [0 1] 
    (iterate (fn [[a b]] [b (+ a b)]))
    (map first)))

;;usage
(take 10 fib)
;;output (0 1 1 2 3 5 8 13 21 34)
(nth fib 9)
;; output  34
 3
Author: Dax Fohl,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-10-15 17:50:31

Una buena definición recursiva es:

(def fib 
  (memoize 
   (fn [x]
       (if (< x 2) 1
       (+ (fib (dec (dec x))) (fib (dec x)))))))

Esto devolverá un término específico. Expandir esto para devolver los primeros n términos es trivial:

(take n (map fib (iterate inc 0)))
 1
Author: U Mad,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-01-22 15:20:48

Para los recién llegados. Respuesta aceptada es una expresión ligeramente complicada de esto:

(defn fib
  ([n]
     (fib [0 1] n))
  ([x, n]
     (if (< (count x) n) 
       (recur (conj x (apply + (take-last 2 x))) n)
       x)))
 0
Author: Ivan Kleshnin,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-10-02 06:59:06

Por lo que vale, lo estos años por lo tanto, aquí está mi solución a 4problema de cierre # 26: Secuencia de Fibonacci

(fn [x] 
    (loop [i '(1 1)]
        (if (= x (count i))
            (reverse i)
            (recur 
              (conj i (apply + (take 2 i)))))))

No creo, de ninguna manera, que este sea el enfoque óptimo o más idiomático. Toda la razón por la que estoy pasando por los ejercicios en 4Clojure ... y reflexionar sobre ejemplos de código de Rosetta Code es aprender clojure.

Por cierto, soy muy consciente de que la secuencia de Fibonacci incluye formalmente 0 ... que este ejemplo debe loop [i '(1 0)] ... pero eso no coincidiría con sus especificaciones. ni pasar sus pruebas unitarias a pesar de cómo han etiquetado este ejercicio. Se escribe como una función recursiva anónima con el fin de cumplir con los requisitos para los ejercicios 4Clojure ... donde tienes que "llenar el espacio en blanco" dentro de una expresión dada. (Estoy encontrando que toda la noción de recursión anónima es un poco un doblador mental; entiendo que la forma especial (loop ... (recur ... está limitada a recursión de cola... pero sigue siendo una sintaxis extraña para me).

Tomaré el comentario de @[Arthur Ulfeldt], con respecto a fib3 en la publicación original, bajo consideración también. Solo he usado Clojure iterate una vez, hasta ahora.

 0
Author: Jim Dennis,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-07-11 23:10:51

Aquí está la función recursiva más corta que se me ocurrió para calcular el enésimo número de Fibonacci:

(defn fib-nth [n] (if (< n 2)
                n
                (+ (fib-nth (- n 1)) (fib-nth (- n 2)))))

Sin embargo, la solución con bucle/recursión debería ser más rápida para todos menos los primeros valores de 'n' ya que Clojure hace una optimización de cola en bucle/recursión.

 0
Author: dilburt,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-06-08 03:38:23