2014-10-09 102 views
0

我写了一个函数,它返回小于20的数字n的素因式分解。函数使用let创建指数列表,并在发现n可被a整除时递增这些指数主要。在我的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) 
+1

引用数据是文字数据。如果您熟悉C语言,则可以从代码中的字面字符数组中识别行为。 * reader *为您创建一个零字面的列表,并且该列表存储在编译后的代码中,因此您只有一个列表,并且它在该函数的所有运行之间共享。 – 2014-10-09 18:17:11

回答

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在顶层。请注意0​​符号,这是一个约定,表明这是一个特殊变量。

+1

更好,因为指数的长度应该与素数'(mapcar(constant 0)* primes *)'的数量相同。 – 2014-10-09 18:14:39

+0

并且不要在这里使用'return-from'。 – uselpa 2014-10-09 18:27:30

+1

是的,循环结构'finally(return exponents)'会更清晰。除此之外,指数列表可以是'(循环指数= ...“。 – 2014-10-09 18:35:46

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。