函数内部变量的范围

时间:2014-10-09 18:08:05

标签: common-lisp literals

我编写了一个返回数字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)

2 个答案:

答案 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。请注意*符号,这些符号表示这是一个特殊变量。