Recursion Clojure中的递归Fibonacci函数
我是clojure的新来者,我想看看到底是怎么回事。我想最好的办法就是写一些简单的代码,我想我应该从斐波那契函数开始 我的第一个努力是:Recursion Clojure中的递归Fibonacci函数,recursion,clojure,fibonacci,tail-recursion,Recursion,Clojure,Fibonacci,Tail Recursion,我是clojure的新来者,我想看看到底是怎么回事。我想最好的办法就是写一些简单的代码,我想我应该从斐波那契函数开始 我的第一个努力是: (defn fib [x, n] (if (< (count x) n) (fib (conj x (+ (last x) (nth x (- (count x) 2)))) n) x)) 不管怎样,为了练习的目的,有人能帮我找到一个更好的纯递归斐波那契函数吗?或者共享更好/不同的功能?回答第一个问题: (defn fib (
(defn fib [x, n]
(if (< (count x) n)
(fib (conj x (+ (last x) (nth x (- (count x) 2)))) n)
x))
不管怎样,为了练习的目的,有人能帮我找到一个更好的纯递归斐波那契函数吗?或者共享更好/不同的功能?回答第一个问题:
(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)))
(定义fib
([n]
(fib[01]n)
([x,n]
(如果(<(计数x)n)
(fib(连体x(+(最后一个x)(第n个x(-(计数x)2)))n)
x) ))
这种类型的函数定义称为多元函数定义。您可以在此处了解更多信息:
至于更好的Fib函数,我认为您的fib3函数非常棒,展示了许多函数编程概念。这既快又酷:
(def fib (lazy-cat [0 1] (map + fib (rest fib))))
发件人:
一个好的递归定义是:
(def fib
(memoize
(fn [x]
(if (< x 2) 1
(+ (fib (dec (dec x))) (fib (dec x)))))))
你可以使用画眉操作符来清理#3(取决于你问谁;有些人喜欢这种风格,有些人讨厌它;我只是指出这是一种选择): 也就是说,我可能会提取
(取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
在Clojure中,实际上建议避免递归,而是使用
循环
和递归
特殊形式。这将看起来像一个递归过程转变为一个迭代过程,避免堆栈溢出并提高性能
下面是一个使用此技术实现斐波那契序列的示例:
(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)))))))
循环
构造采用一系列绑定,这些绑定提供初始值和一个或多个主体形式。在任何这些主体形式中,调用recur
都会导致使用提供的参数递归调用循环。对于后来者。公认的答案是一个稍微复杂的表达:
(定义fib
([n]
(fib[01]n)
([x,n]
(如果(<(计数x)n)
(重复(联合x(应用+(取最后2 x)))n)
x) ))
鉴于这些年来的价值,以下是我的解决方案
无论如何,我不认为这是最佳或最惯用的方法。我在4Clojure做练习的全部原因。。。仔细研究代码示例是为了学习
顺便说一句,我很清楚斐波那契序列形式上包括0。。。这个例子应该循环[i'(10)]
。。。但这不符合他们的规范,也不能通过他们的单元测试,不管他们如何标记这个练习。它是作为匿名递归函数编写的,以符合4Clojure练习的要求。。。你必须在一个给定的表达式中“填空”。(我发现匿名递归的整个概念有点令人费解;我知道(循环…(recur…
特殊形式被限制为…,但对我来说它仍然是一种奇怪的语法)
我还将考虑[Arthur Ulfeldt]关于原始帖子中fib3的评论。到目前为止,我只使用了Clojure的
迭代
一次。以下是我提出的计算第n个Fibonacci数的最短递归函数:
(defn fib-nth [n] (if (< n 2)
n
(+ (fib-nth (- n 1)) (fib-nth (- n 2)))))
(定义光纤[n](如果(
但是,除了“n”的前几个值之外,使用循环/递归的解决方案应该更快,因为Clojure对循环/递归进行尾部优化。这是我的方法
(defn fibonacci-seq [n]
(cond
(= n 0) 0
(= n 1) 1
:else (+ (fibonacci-seq (- n 1)) (fibonacci-seq (- n 2)))
)
)
如果我理解正确的话,它看起来像是重载函数的一个别致的名称。非常好用,谢谢。“Multi-arity”比“重载”更具体。“Multi-arity”表示“通过参数的数量区分”,而“重载”通常表示“通过参数的数量或类型区分”所以多重算术是重载方法的一个子集。我们如何编写一个没有递归的不可变版本呢?fib3是这些方法中最难懂的,很难理解,但非常有趣。(def fib(lazy cat[01](map+fib(rest fib)))和(取5个fib)将返回前5个术语。同样,我正在努力将其作为一个函数来编写:(defn fib([n](take n fib))([](lazy cat[01](map+fib(rest fib‘‘‘‘‘‘‘‘‘))不起作用。如果理解这5行代码的困难(我在谈论树算法)不会给你带来任何关于这种语言的危险信号……另外,你能数一数代码中的分配数吗?它非常高。仅仅因为运行时线性扩展并不意味着它很快。@richc它最初定义为一个变量,但你将它改为一个函数。所以你必须将
fib
的所有用法改为(fib)
在您的实现中。FWIW:(fn[n](先取n(映射)(迭代(fn[[ab]]][b(+ab)])'(11()())(1))())()()))。
(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
(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)))))))
(fn [x]
(loop [i '(1 1)]
(if (= x (count i))
(reverse i)
(recur
(conj i (apply + (take 2 i)))))))
(defn fib-nth [n] (if (< n 2)
n
(+ (fib-nth (- n 1)) (fib-nth (- n 2)))))
(defn fibonacci-seq [n]
(cond
(= n 0) 0
(= n 1) 1
:else (+ (fibonacci-seq (- n 1)) (fibonacci-seq (- n 2)))
)
)