好的,我一直在学习COMMON LISP编程,我正在研究一个非常简单的程序来计算给定整数的阶乘。简单,对吧?
到目前为止,这是代码:
(write-line "Please enter a number...")
(setq x (read))
(defun factorial(n)
(if (= n 1)
(setq a 1)
)
(if (> n 1)
(setq a (* n (factorial (- n 1))))
)
(format t "~D! is ~D" n a)
)
(factorial x)
问题是,当我在CodeChef或Rexter.com上运行时,我收到类似的错误:" NIL不是数字。"
我尝试使用cond
代替if
无效。
作为旁注,最让人眼花缭乱的是,我已经看到很多地方写这样的代码:
(defun fact(n)
(if (= n 1)
1
(* n (fact (- n 1)))))
这对我来说甚至没有意义,1只是漂浮在那里而周围没有括号。但是,稍微修补一下(在函数外部写下额外的行)我可以让它执行(同样令人困惑!)。
但那不是我想要的!我喜欢使用阶乘函数来打印/返回值,而不必在其外部执行其他代码。
我做错了什么?
答案 0 :(得分:3)
实际上需要使用FINISH-OUTPUT
刷新可移植代码中的I / O缓冲区 - 否则Lisp可能想要读取某些内容并且尚未打印提示。最好用SETQ
替换LET
,因为SETQ不会引入变量,只是设置它。
(defun factorial (n)
(if (= n 1)
1
(* n (factorial (- n 1)))))
(write-line "Please enter a number...")
(finish-output) ; this makes sure the text is printed now
(let ((x (read)))
(format t "~D! is ~D" x (factorial x)))
答案 1 :(得分:2)
Common Lisp旨在编译。因此,如果您需要全局或局部变量,则需要在设置它们之前对其进行定义。
在第2行,您为x
提供了一个值,但尚未通过该名称声明存在变量。虽然名称(defvar x)
被认为是单一的,但您可以x
执行此操作。当您尝试设置尚未定义的内容时,许多实现都会发出警告并自动创建全局变量。
在factorial
功能中,您尝试设置a
。这被视为错误或全局变量。请注意,在您的递归调用中,您正在更改a
的值,尽管这实际上并不会对您的其余函数产生太大影响。你的功能也不是可重入的,没有理由这样做。您可以使用let
引入局部变量。或者,您可以将其作为(n &aux a)
添加到lambda列表中。其次,您的阶乘函数不会返回有用的值,因为format
不会返回有用的值。在(隐式)预测中的Common Lisp中,返回最终表达式的值。您可以通过在a
下方的行中添加format
来解决此问题。
对于跟踪执行,您可以(trace factorial)
自动打印正确的跟踪信息。然后你可以摆脱你的format
陈述。
最后值得注意的是,整个功能非常单一。你的语法不正常。 Common Lisp实现带有漂亮的打印机。 Emacs也是(绑定到M-q
)。通常不会对全局变量进行大量读取和设置(偶尔在repl处除外)。 Lisp并不真正用于这种风格的脚本,并且具有更好的控制范围的机制。其次,人们通常不会在这样的函数中使用如此多的状态变异。这是做与阶乘的不同方式:
(defun factorial (n)
(if (< n 2)
1
(* n (factorial (1- n)))))
递归地尾巴:
(defun factorial (n &optional (a 1))
(if (< n 2) a (factorial (1- n) (* a n))))
迭代(带打印):
(defun factorial (n)
(loop for i from 1 to n
with a = 1
do (setf a (* a i))
(format t “~a! = ~a~%” i a)
finally (return a)))
答案 2 :(得分:2)
在回答你的问题之前,我想告诉你一些关于Lisp的基本知识。 (最后解决了你的解决方案)
在Lisp中,每个函数的输出都是&#34;在函数&#34;中执行的最后一行。除非您使用某些语法操作,例如&#34; return&#34;或&#34; return-from&#34;,这不是Lisp-way。
(格式t&#34;您的字符串&#34;)将始终返回&#39; NIL作为其输出。但在返回输出之前,此功能&#34;打印&#34;字符串也是。 但格式功能的输出是“NIL。
现在,代码的问题是函数的输出。同样,输出将是最后一行,在您的情况下:
(format t "~D! is ~D" n a)
这将返回&#39; NIL。
要说服自己,请按照定义的函数运行以下命令:
(equal (factorial 1) 'nil)
返回:
1! is 1
T
所以&#34;打印&#34;你的字符串,然后输出T.因此,你的函数的输出确实是'NIL。
因此,当您输入任何大于1的数字时,递归调用将运行并作为输入1到达结尾并返回&#39; NIL。 然后尝试执行此操作:
(setq a (* n (factorial (- n 1))))
*的第二个参数是&#39; NIL,因此是错误。
快速解决您的解决方案是将最后一行添加为输出:
(write-line "Please enter a number...")
(setq x (read))
(defun factorial(n)
(if (= n 1)
(setq a 1)
)
(if (> n 1)
(setq a (* n (factorial (- n 1))))
)
(format t "~D! is ~D" n a)
a ;; Now this is the last line, so this will work
)
(factorial x)
Neater代码(具有类似Lisp的缩进)
(defun factorial (n)
(if (= n 1)
1
(* n (factorial (- n 1)))))
(write-line "Please enter a number...")
(setq x (read))
(format t "~D! is ~D" x (factorial x))
答案 3 :(得分:1)
您可以将其拆分为多个部分,如下所示:
(defun prompt (prompt-str)
(write-line prompt-str *query-io*)
(finish-output)
(read *query-io*))
(defun factorial (n)
(cond ((= n 1) 1)
(t (* n
(factorial (decf n)))))
(defun factorial-driver ()
(let* ((n (prompt "Enter a number: "))
(result (factorial n)))
(format *query-io* "The factorial of ~A is ~A~%" n result)))
然后将整个事情作为(factorial-driver)
运行。
示例互动:
CL-USER 54 > (factorial-driver)
Enter a number:
4
The factorial of 4 is 24