繁体   English   中英

Clojure 中的递归斐波那契函数

[英]a recursive Fibonacci function in Clojure

我是 clojure 的新手,他想看看有什么大惊小怪的。 确定感受它的最佳方法是编写一些简单的代码,我想我会从斐波那契函数开始。

我的第一个努力是:

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

无论如何,更多的是为了练习而不是其他任何事情,任何人都可以用更好的纯递归斐波那契函数版本来帮助我吗? 或者也许共享一个更好/不同的功能?

回答你的第一个问题:

(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 函数非常棒,展示了很多函数式编程概念。

这又快又酷:

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

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

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

以下是如何使用此技术实现斐波那契数列的示例:

(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的调用将导致使用提供的参数递归调用循环。

您可以使用 thrush 运算符稍微清理一下 #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

一个好的递归定义是:

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

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

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

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

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

就其价值而言,这些年来,这是我对4Closure 问题 #26:斐波那契数列的解决方案

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

无论如何,我不认为这是最佳或最惯用的方法。 我在 4Clojure 上进行练习……并仔细研究Rosetta Code 中的代码示例的全部原因是学习

顺便说一句,我很清楚斐波那契数列形式上包含 0 ......这个例子应该loop [i '(1 0)] ......但这不符合他们的规范。 尽管他们如何标记这个练习,也没有通过他们的单元测试。 它被编写为匿名递归函数,以符合 4Clojure 练习的要求……您必须在给定的表达式中“填空”。 (我发现匿名递归的整个概念有点令人费解;我知道(loop ... (recur ...特殊形式被限制为......但它仍然很奇怪语法给我)。

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

这是我为计算第 n 个斐波那契数而提出的最短递归函数:

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

但是,除了 'n' 的前几个值之外,循环/递归的解决方案应该更快,因为 Clojure 对循环/递归进行了尾端优化。

这是我的方法

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM