使用递归在Lisp中生成Fibonacci系列?

时间:2014-04-14 16:55:36

标签: recursion lisp clisp

我是LISP的新手。我正在尝试在CLISP中编写一个函数来生成前n个Fibonacci系列。

这是我到目前为止所做的。

(defun fibonacci(n)
  (cond
    ((eq n 1) 0)
    ((eq n 2) 1)
    ((+ (fibonacci (- n 1)) (fibonacci (- n 2))))))))

该程序打印第n个Fibonacci系列。我正在尝试修改它以便打印系列,而不仅仅是第n个术语。

只使用基本函数,是否可以在一个递归函数中完成?

3 个答案:

答案 0 :(得分:10)

是:

(defun fibonacci (n &optional (a 0) (b 1) (acc ()))
  (if (zerop n)
      (nreverse acc)
      (fibonacci (1- n) b (+ a b) (cons a acc))))

(fibonacci 5) ; ==> (0 1 1 2 3)

它背后的逻辑是你需要知道前两个数字才能生成下一个数字。

a     0 1 1 2 3 5 ...
b     1 1 2 3 5 8 ...
new-b 1 2 3 5 8 13 ...     

我不会只返回一个结果,而是积累所有a - s直到n为零。

编辑如果没有反向,它的效率会更低一些:

(defun fibonacci (n &optional (a 0) (b 1))
  (if (zerop n)
      nil
      (cons a (fibonacci (1- n) b (+ a b)))))

(fibonacci 5) ; ==> (0 1 1 2 3)

答案 1 :(得分:3)

  

程序打印第n个Fibonacci系列。

此程序无法打印任何内容。如果您正在查看输出,可能是因为您从read-eval- print -loop(REPL)调用它,它读取表单,对其进行评估,然后打印结果。例如,您可能正在做:

CL-USER> (fibonacci 4)
2

如果你把这个电话换成其他东西,你会发现它没有打印任何东西:

CL-USER> (progn (fibonacci 4) nil)
NIL

由于你已经写了这篇文章,因此你很难修改它来打印每个斐波那契数字,因为你做了很多冗余计算。例如,调用

(fibonacci (- n 1))

将计算(fibonacci (- n 1)),但直接调用

也是如此
(fibonacci (- n 2))

这意味着您可能不希望每次调用fibonacci来打印整个序列。但是,如果您这样做,请注意(print x)会返回x的值,因此您只需执行以下操作:

(defun fibonacci(n)
  (cond
    ((eq n 1) 0)
    ((eq n 2) 1)
    ((print (+ (fibonacci (- n 1)) (fibonacci (- n 2)))))))
CL-USER> (progn (fibonacci 6) nil)

1 
2 
1 
3 
1 
2 
5 
NIL

由于存在冗余计算,您会在那里看到一些重复的部分。但是,您可以更有效地计算系列,从前两个数字开始,并向上计数:

(defun fibonacci (n)
  (do ((a 1 b)
       (b 1 (print (+ a b)))
       (n n (1- n)))
      ((zerop n) b)))
CL-USER> (fibonacci 6)

2 
3 
5 
8 
13 
21 

答案 2 :(得分:2)

保留您使用的基本结构的一个选项是将另一个标志传递给该函数,告知您是否要打印:

(defun fibo (n printseq)
  (cond
    ((= n 1) (if printseq (print 0) 0))
    ((= n 2) (if printseq (print 1) 1))
    (T
     (let ((a (fibo (- n 1) printseq))
           (b (fibo (- n 2) NIL)))
       (if printseq (print (+ a b)) (+ a b))))))

这个想法是,当你只在第一个传递两个递归调用时,传递关于进行打印的标记,而在第二个调用中,你只需传递NIL以避免再次打印。