清理Clojure函数

清理Clojure函数,clojure,refactoring,fibonacci,Clojure,Refactoring,Fibonacci,我来自命令式编程语言,我正试图对Clojure有所了解,希望能利用它的多线程功能。 其中一个问题是编写一个函数来生成长度为N的Fibonacci数列表,其中N>1。我写了一个函数,但鉴于我有限的背景,我想了解一下这是否是Clojure最好的方法。代码如下: (fn fib [x] (cond (= x 2) '(1 1) :else (reverse (conj (reverse (fib (dec x))) (+ (last (fib

我来自命令式编程语言,我正试图对Clojure有所了解,希望能利用它的多线程功能。
其中一个问题是编写一个函数来生成长度为N的Fibonacci数列表,其中N>1。我写了一个函数,但鉴于我有限的背景,我想了解一下这是否是Clojure最好的方法。代码如下:

(fn fib [x] (cond 
               (= x 2) '(1 1)
            :else   (reverse (conj (reverse (fib (dec x))) (+ (last (fib (dec x))) (-> (fib (dec x)) reverse rest first))))
          ))

这里有一种方法可以让你接触到一些惰性序列,尽管它确实不是计算斐波那契序列的最佳方法

给出了斐波那契序列的定义,我们可以看到它是通过重复地将相同的规则应用于
'(11)
的基本情况而建立起来的。Clojure函数
iterate
听起来似乎适合这样做:

user>(文档迭代)
-------------------------
clojure.core/iterate
([f x])
返回x,(fx)、(f(fx))等的惰性序列。f必须没有副作用
因此,对于我们的函数,我们需要一些东西,它接受我们迄今为止计算的值,对最近的两个值求和,并返回一个新值和所有旧值的列表

(fn [[x y & _ :as all]] (cons (+ x y) all))
此处的参数列表仅意味着
x
y
将绑定到作为函数参数传递的列表中的前两个值,包含前两个后所有参数的列表将绑定到
\uu
,作为参数传递给函数的原始列表可以通过
all
引用

现在,
iterate
将返回一个无限的中间值序列,因此在我们的例子中,我们希望将它包装成只返回我们感兴趣的值的东西;延迟求值将停止正在求值的整个无限序列

(defn fib [n]
   (nth (iterate (fn [[x y & _ :as all]] (cons (+ x y) all)) '(1 1)) (- n 2)))
还要注意,这将以与实现相反的顺序返回结果;当然,用
reverse
解决这个问题很简单

编辑:或者,正如amalloy所说,通过使用向量:

(defn fib [n]
   (nth (iterate (fn [all]
                   (conj all (->> all (take-last 2) (apply +)))) [1 1])
        (- n 2)))

下面是一个我非常喜欢的斐波那契版本(我从clojure wikibook中获取了实现:)

它的工作原理是这样的:假设你已经有了无限的斐波那契数序列。如果取序列的尾部并将其按元素添加到原始序列,则得到(序列尾部的尾部)斐波那契序列

0 1 1 2 3 5 8 ...
1 1 2 3 5 8 ...
-----------------
1 2 3 5 8 13 ... 
因此,您可以使用它来计算序列。您需要两个初始元素[0 1](或[1 1],具体取决于序列的起始位置),然后只需在添加元素的两个序列上进行映射即可。注意,这里需要惰性序列

我认为这是最优雅和(至少对我来说)让人心旷神怡的实现

编辑:fib函数为

 (defn fib [n] (nth fib-seq n)) 
最惯用的“函数式”方法可能是创建一个fibonacci数的无限延迟序列,然后提取前n个值,即:

(take n some-infinite-fibonacci-sequence)
下面的链接有一些非常有趣的方法,可以沿着这些线生成fibonnaci序列:

最后,这里是另一个有趣的实现:

 (defn fib [n]
   (let [next-fib-pair (fn [[a b]] [b (+ a b)])
         fib-pairs (iterate next-fib-pair [1 1])
         all-fibs (map first fib-pairs)]
     (take n all-fibs)))


 (fib 6)
 => (1 1 2 3 5 8)

它并没有尽可能简洁,但很好地演示了如何使用Clojure的解构、惰性序列和高阶函数来解决这个问题。

请参见Stu Halloway编写Clojure程序时Christophe Grand的Fibonacci解决方案。这是我见过的最优雅的解决方案

(defn fibo [] (map first (iterate (fn [[a b]] [b (+ a b)]) [0 1])))
(take 10 (fibo))
也看到


Clojure在最终构建cons链时,不需要重复使用
reverse
ing cons链的公共Lisp反模式,因为它有向右而不是向左扩展的向量。感谢您的详细回复。创建惰性列表和使用“n”函数仅根据需要进行计算的极好示例。更多答案:
(defn fibo [] (map first (iterate (fn [[a b]] [b (+ a b)]) [0 1])))
(take 10 (fibo))