如何在Clojure中为变量重新分配另一个值

时间:2018-10-10 07:05:52

标签: clojure fibonacci

我正在为斐波那契数列执行此Clojure程序。

(def fibonacci []
  (def a 0)
  (def b 0)
  (def c 1)
  (def n (atom 0))
  (while (<= @n 10)
    (do
      (def c (+ a b))
      (def a b)
      (def b c)
      (println '(a b c))
      )(swap! n inc)))
(fibonacci)

我收到此错误:

CompilerException java.lang.RuntimeException: Too many arguments to def, compiling:(C:\Users\user\AppData\Local\Temp\form-init4960759414983563364.clj:1:1) 
CompilerException java.lang.RuntimeException: Unable to resolve symbol: fibonacci in this context, compiling:(C:\Users\user\AppData\Local\Temp\form-init4960759414983563364.clj:13:1) 

我不知道如何重新分配变量abc。另外,请提出程序中需要进行的任何更正。

2 个答案:

答案 0 :(得分:2)

首先,让我们开始编写代码。定义函数的第一个def应该是defn

(defn fibonacci []
  (def a 0)
  (def b 0)
  (def c 1)
  (def n (atom 0))
  (while (<= @n 10)
    (do
      (def c (+ a b))
      (def a b)
      (def b c)
      (println '(a b c))
      )(swap! n inc)))

它编译。试试吧:

> (fibonacci)

(a b c)
(a b c)
...
(a b c)
=> nil

不是您想要的。麻烦的是(a b c)前面的引号,它返回列表中未评估的元素。我们必须取消引号,但我们不希望将a视为运算符。因此放入list

(defn fibonacci []
  (def a 0)
  (def b 0)
  (def c 1)
  (def n (atom 0))
  (while (<= @n 10)
    (do
      (def c (+ a b))
      (def a b)
      (def b c)
      (println (list a b c))
      )(swap! n inc)))

让我们运行它:

> (fibonacci)

(0 0 0)
(0 0 0)
...
(0 0 0)
=> nil

嗯,我们有数字,尽管不是我们想要的数字。问题在于,第一个(def c (+ a b))会擦除唯一的非零值。我们应该从b的{​​{1}}开始,然后暂时使用1

c

现在...

(defn fibonacci []
  (def a 0)
  (def b 1)
  (def n (atom 0))
  (while (<= @n 10)
    (do
      (def c (+ a b))
      (def a b)
      (def b c)
      (println (list a b c))
      )(swap! n inc)))

好啦!

但是我们可以看到> (fibonacci) (1 1 1) (1 2 2) (2 3 3) (3 5 5) (5 8 8) (8 13 13) (13 21 21) (21 34 34) (34 55 55) (55 89 89) (89 144 144) => nil b是相同的,只是预测下一个c

有一个更大的问题。使用可变变量不是Clojure的方法。函数打印所计算的内容也不是惯用的:更好地将结果显示为序列,我们可以根据自己的喜好进行处理。

我们可以这样定义整个斐波那契数列:

a

随心所欲地发展:

(defn fibonacci []
  (map second
       (iterate
         (fn [[a b]] [b (+ a b)])
         [0 1])))

答案 1 :(得分:0)

正如注释中已经指出的那样,您通常使用命令式语言 通过更改变量的值来解决此类问题。但是在功能上 Clojure之类的语言专注于不变性,函数调用和 递归。虽然您可以找到解决方案 bindingsset!之类的东西,不是样式 在其中应该使用Clojure进行编程(至少针对此类问题)。

因此,用于迭代计算斐波那契数的函数可能看起来像 像这样(另请参见 this section in SICP Distilled):

(defn fibonacci
  [n]
  (letfn [(fib-iter [a b count]
            (if (= count 0)
              b
              (fib-iter (+ a b) a (- count 1))))]
    (fib-iter 1 0 n)))

fib-iter是一个局部函数,递归调用自身(通过 tail call recursion)。但是按照书面 here

  

由于Clojure使用Java调用约定,因此它不能,也不会使     相同的尾部调用优化保证。相反,它提供了递归     特殊运算符,它通过重新绑定和执行常数空间递归循环     跳到最近的封闭环或功能框。

因此,您可以将尾叫中的fib-iter替换为 recur得到一个 迭代过程,或者您可以使用loop form 从而简化了代码:

(defn fibonacci
  [n]
  (loop [a 1
         b 0
         count n]
    (if (= count 0)
      b
      (recur (+ a b) a (- count 1)))))