我是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个术语。
只使用基本函数,是否可以在一个递归函数中完成?
答案 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以避免再次打印。