我编写了一个返回数字n小于20的素数分解的函数。该函数使用let创建一个指数列表,当它发现n可以被素数整除时递增这些指数。
在我的Lisp解释器(gcl和clisp)中,当我调用以下函数一次时,我得到了正确的因子分解,但是当我第二次调用它时,我得到了第一个和第二个函数的因子的总和 - 但exponents
的范围是否仅限于let !?的范围内?为什么没有exponents
重新分配值'(0 0 0 0 0 0 0 0)
?如何重新编写此函数以使其能够承受多次调用?
(setf primes '(2 3 5 7 11 13 17 19))
(defun factorize (n)
(let ((exponents '(0 0 0 0 0 0 0 0)))
(loop for i from 0 to (- (length primes) 1) do
(loop while (and (= (mod n (nth i primes)) 0)
(not (= n 1))) do
(incf (nth i exponents))
(setf n (/ n (nth i primes)))))
(return-from factorize exponents)))
输出:
>(factorize 10) ;; first time
(1 0 1 0 0 0 0 0) ;; 2^1*5*1 = 10, correct
>(factorize 10)
(2 0 2 0 0 0 0 0) ;; wrong
>(factorize 10)
(3 0 3 0 0 0 0 0)
答案 0 :(得分:2)
另一个有趣的基于循环的解决方案,它不涉及对列表的随机访问(效率相当低)。这也使用truncate,它返回一个商和一个余数作为多个值(可以使用multiple-value-list收集,并使用循环进行解构。因为您可以遍历素数,你可以像这样收集指数:
(defparameter *primes* '(2 3 5 7 11 13 17 19))
(defun factorize (n)
(loop
for p in *primes*
collect (loop
for (quotient remainder) = (multiple-value-list (truncate n p))
while (zerop remainder)
count (setf n quotient))))
CL-USER> (factorize 10)
(1 0 1 0 0 0 0 0)
CL-USER> (factorize 12)
(2 1 0 0 0 0 0 0)
CL-USER> (factorize (* 19 19 11 5 7 2 2))
(2 0 1 1 1 0 0 2)
当然,一旦你在列表中迭代并为每个其他值收集一个值,你就可以使用mapcar。
答案 1 :(得分:1)
列表'(0 0 0 0 0 0 0 0)存储为文字,修改文字是未定义的行为。你应该使用
(let ((exponents (copy-list '(0 0 0 0 0 0 0 0))))
每次需要时复制文字,或者
(let ((exponents (list 0 0 0 0 0 0 0 0)))
顺便说一下,使用
(defparameter *primes* '(2 3 5 7 11 13 17 19))
而不是顶级的setf
。请注意*
符号,这些符号表示这是一个特殊变量。