【问题标题】:a recursive Fibonacci function in ClojureClojure 中的递归斐波那契函数
【发布时间】:2012-02-14 22:24:15
【问题描述】:

我是 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]))))

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

【问题讨论】:

  • fib3 是其中最接近 Clojure 的

标签: recursion clojure fibonacci


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

【讨论】:

  • 如果我理解正确的话,看起来像是一个重载函数的花哨名称。效果很好,谢谢。
  • “Multi-arity”比“overloaded”更具体。 “多参数”意味着“通过参数的数量来区分”,而“重载”通常意味着“通过参数的数量或类型来区分”。所以多参数是重载方法的一个子集。
  • 我们如何编写一个不使用递归的不可变版本?
【解决方案2】:

这又快又酷:

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

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

【讨论】:

  • 感谢 nickik,虽然很难理解,但很有趣。
  • (def fib (lazy-cat [0 1] (map + fib (rest fib)))) 和 (take 5 fib) 将返回前 5 个术语。我再次努力将其写成一个函数: (defn fib ([n] (take n fib)) ([] (lazy-cat [0 1] (map + fib (rest fib))))) 没有不行。
  • 如果理解这 5 行代码的困难(我说的是树算法)不会对你提出任何关于这种语言的危险信号......另外,你能数一下吗?该代码中的分配数量?实在是太高了仅仅因为运行时间是线性扩展的,并不意味着它很快。
  • @richc 本来是定义为var的,你改成函数了。因此,您必须在您的实现中将所有fib 的使用更改为(fib)
【解决方案3】:

一个好的递归定义是:

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

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

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

【讨论】:

    【解决方案4】:

    您可以使用画眉操作符稍微清理一下#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
    

    【讨论】:

      【解决方案5】:

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

      【讨论】:

        【解决方案6】:

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

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

        【讨论】:

          【解决方案7】:

          对于它的价值,这些年来,这是我对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 的代码示例的全部原因是为了学习

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

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

          【讨论】:

          • FWIW: (fn [n] (take n (map first (iterate (fn [[ab]] [b (+ ab)]) '(1 1))))) ...也适用于这个问题的 4Clojure 形式。
          【解决方案8】:

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

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

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

          【讨论】:

            【解决方案9】:

            这是我的方法

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

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 2021-12-01
              • 2020-03-21
              • 2010-12-03
              • 2020-12-18
              • 2021-02-13
              • 2021-11-17
              相关资源
              最近更新 更多