Clojure中的递归Fibonacci函数

时间:2012-01-20 10:28:59

标签: recursion clojure fibonacci

我是clojure的新手,他想看看所有的大惊小怪。找出感受它的最好方法是编写一些简单的代码,我想我会从Fibonacci函数开始。

我的第一个努力是:

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

要使用它,我需要在调用函数时使用[0 1]对x进行种子处理。我的问题是,如果不将它包装在一个单独的函数中,是否可以编写一个只返回元素数量的函数?

做一些阅读让我有了一些更好的方法来实现相同的功能:

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

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

无论如何,更多的是为了锻炼而不是其他任何东西,任何人都可以帮助我使用纯粹递归的Fibonacci函数的更好版本吗?或者可能分享一个更好/不同的功能?

9 个答案:

答案 0 :(得分:17)

首先回答你的问题:

(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)))

这种类型的函数定义称为多元函数定义。您可以在此处了解详情:http://clojure.org/functional_programming

至于更好的Fib函数,我认为你的fib3函数非常棒,并展示了很多函数式编程概念。

答案 1 :(得分:7)

这很快又很酷:

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

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

答案 2 :(得分:6)

在Clojure中,实际上建议避免递归,而是使用looprecur特殊形式。这将看起来像递归过程变成迭代过程,避免堆栈溢出并提高性能。

以下是使用此技术实现Fibonacci序列的示例:

(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)))))))

loop构造采用一系列绑定,提供初始值和一个或多个正文表单。在任何这些正文形式中,对recur的调用将导致使用提供的参数递归调用循环。

答案 3 :(得分:3)

您可以使用鹅口疮操作员来清理#3(取决于您的问题;有些人喜欢这种风格,有些人讨厌它;我只是指出它是一种选择):

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

那就是说,我可能会提取(take n)并让fib函数成为一个懒惰的无限序列。

(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

答案 4 :(得分:2)

一个好的递归定义是:

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

这将返回一个特定的术语。扩展这个以返回前n个术语是微不足道的:

(take n (map fib (iterate inc 0)))

答案 5 :(得分:0)

对于后来者。接受的答案是一个稍微复杂的表达:

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

答案 6 :(得分:0)

对于它的价值,这些年来,这是我对4Closure Problem #26: Fibonacci Sequence的解决方案

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

我绝不认为这是最佳或最惯用的方法。我在4Clojure上完成练习的全部原因......并且正在考虑Rosetta Code中的代码示例,即学习

顺便说一句,我很清楚Fibonacci序列正式包含0 ......这个例子应该loop [i '(1 0)] ......但是这与他们的规范不符。尽管他们已经标记了这个练习,但也没有通过他们的单元测试。它被编写为匿名递归函数,以符合4Clojure练习的要求......您必须在给定表达式中“填空”。 (我发现匿名递归的整个概念有点像弯曲;我得到(loop ... (recur ...特殊形式被约束到 ......但它对我来说仍然是一个奇怪的语法)。

我还会考虑@ [Arthur Ulfeldt]关于原始帖子中的fib3的评论。到目前为止,我只使用过一次Clojure iterate

答案 7 :(得分:0)

这是我用来计算第n个Fibonacci数的最短递归函数:

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

然而,除了前几个值之外,所有的循环/递归解决方案应该更快。&#39; n&#39;因为Clojure在循环/重复上进行尾端优化。

答案 8 :(得分:0)

这是我的方法

(defn fibonacci-seq [n]
  (cond
    (= n 0) 0
    (= n 1) 1
    :else (+ (fibonacci-seq (- n 1)) (fibonacci-seq (- n 2)))
    )
  )