简体   繁体   English

Clojure 中的递归斐波那契函数

[英]a recursive Fibonacci function in Clojure

I'm a newcomer to clojure who wanted to see what all the fuss is about.我是 clojure 的新手,他想看看有什么大惊小怪的。 Figuring the best way to get a feel for it is to write some simple code, I thought I'd start with a Fibonacci function.确定感受它的最佳方法是编写一些简单的代码,我想我会从斐波那契函数开始。

My first effort was:我的第一个努力是:

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

To use this I need to seed x with [0 1] when calling the function.要使用它,我需要在调用函数时用 [0 1] 为 x 设置种子。 My question is, without wrapping it in a separate function, is it possible to write a single function that only takes the number of elements to return?我的问题是,如果不将其包装在单独的函数中,是否可以编写一个只需要返回元素数量的函数?

Doing some reading around led me to some better ways of achieving the same funcionality:做一些阅读让我找到了一些更好的方法来实现相同的功能:

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

and

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

Anyway, more for the sake of the exercise than anything else, can anyone help me with a better version of a purely recursive Fibonacci function?无论如何,更多的是为了练习而不是其他任何事情,任何人都可以用更好的纯递归斐波那契函数版本来帮助我吗? Or perhaps share a better/different function?或者也许共享一个更好/不同的功能?

To answer you first question:回答你的第一个问题:

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

This type of function definition is called multi-arity function definition.这种类型的函数定义称为多元函数定义。 You can learn more about it here: http://clojure.org/functional_programming您可以在此处了解更多信息: http : //clojure.org/functional_programming

As for a better Fib function, I think your fib3 function is quite awesome and shows off a lot of functional programming concepts.至于更好的 Fib 函数,我觉得你的 fib3 函数非常棒,展示了很多函数式编程概念。

This is fast and cool:这又快又酷:

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

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

In Clojure it's actually advisable to avoid recursion and instead use the loop and recur special forms.在 Clojure 中,实际上建议避免递归,而是使用looprecur特殊形式。 This turns what looks like a recursive process into an iterative one, avoiding stack overflows and improving performance.这将看似递归的过程变成了迭代过程,从而避免堆栈溢出并提高性能。

Here's an example of how you'd implement a Fibonacci sequence with this technique:以下是如何使用此技术实现斐波那契数列的示例:

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

The loop construct takes a series of bindings, which provide initial values, and one or more body forms. loop结构采用一系列绑定,提供初始值和一个或多个主体形式。 In any of these body forms, a call to recur will cause the loop to be called recursively with the provided arguments.在任何这些主体形式中,对recur的调用将导致使用提供的参数递归调用循环。

You can use the thrush operator to clean up #3 a bit (depending on who you ask; some people love this style, some hate it; I'm just pointing out it's an option):您可以使用 thrush 运算符稍微清理一下 #3(取决于您问的是谁;有些人喜欢这种风格,有些人讨厌它;我只是指出这是一种选择):

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

That said, I'd probably extract the (take n) and just have the fib function be a lazy infinite sequence.也就是说,我可能会提取(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

A good recursive definition is:一个好的递归定义是:

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

This will return a specific term.这将返回一个特定的术语。 Expanding this to return first n terms is trivial:扩展它以返回前 n 项是微不足道的:

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

For latecomers.对于后来者。 Accepted answer is a slightly complicated expression of this:接受的答案是一个稍微复杂的表达:

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

For what it's worth, lo these years hence, here's my solution to 4Closure Problem #26: Fibonacci Sequence就其价值而言,这些年来,这是我对4Closure 问题 #26:斐波那契数列的解决方案

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

I don't, by any means, think this is the optimal or most idiomatic approach.无论如何,我不认为这是最佳或最惯用的方法。 The whole reason I'm going through the exercises at 4Clojure ... and mulling over code examples from Rosetta Code is to learn .我在 4Clojure 上进行练习……并仔细研究Rosetta Code 中的代码示例的全部原因是学习

Incidentally I'm well aware that the Fibonacci sequence formally includes 0 ... that this example should loop [i '(1 0)] ... but that wouldn't match their spec.顺便说一句,我很清楚斐波那契数列形式上包含 0 ......这个例子应该loop [i '(1 0)] ......但这不符合他们的规范。 nor pass their unit tests despite how they've labelled this exercise.尽管他们如何标记这个练习,也没有通过他们的单元测试。 It is written as an anonymous recursive function in order to conform to the requirements for the 4Clojure exercises ... where you have to "fill in the blank" within a given expression.它被编写为匿名递归函数,以符合 4Clojure 练习的要求……您必须在给定的表达式中“填空”。 (I'm finding the whole notion of anonymous recursion to be a bit of a mind bender; I get that the (loop ... (recur ... special form is constrained to ... but it's still a weird syntax to me). (我发现匿名递归的整个概念有点令人费解;我知道(loop ... (recur ...特殊形式被限制为......但它仍然很奇怪语法给我)。

I'll take @[Arthur Ulfeldt]'s comment, regarding fib3 in the original posting, under consideration as well.我也会考虑@[Arthur Ulfeldt] 关于原始帖子中 fib3 的评论。 I've only used Clojure's iterate once, so far.到目前为止,我只使用了一次 Clojure 的iterate

Here is the shortest recursive function I've come up with for computing the nth Fibonacci number:这是我为计算第 n 个斐波那契数而提出的最短递归函数:

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

However, the solution with loop/recursion should be faster for all but the first few values of 'n' since Clojure does tail-end optimization on loop/recur.但是,除了 'n' 的前几个值之外,循环/递归的解决方案应该更快,因为 Clojure 对循环/递归进行了尾端优化。

this is my approach这是我的方法

(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