将数字列表分解为数字

时间:2014-05-26 14:46:15

标签: lisp common-lisp

我正在尝试从数字列表开始创建一个数字列表。 例如,如果头部>> 10并且将1加到下一个元素,我想通过将列表的头部划分为10来将(11 4 6)分解为(1 5 6)。
我的代码看起来像这样

(defun createparameters (l)
  (cond ((null l) l)
        ((> 9 (car l)) (setf (car l) (mod (car l ) 10))
                       (setf (cadr l) (+ (cadr l) 1)))
        (t (createparameters (cdr l)))))

但它不会改变我的引用列表。 非常感谢帮助。

2 个答案:

答案 0 :(得分:2)

如果第一个元素大于9,你写道你想要完成操作,但在你的代码中,你正在做相反的事情。 (> 9 (car l))与中缀9 > (car l)相同,因此当第一个元素为8或更低时,您可以执行此操作。

以下是代码的功能版本,它继续处理下一个子列表:

(defun createparameters (l)
  (cond ((null l) l)
        ((and (consp l) (< 9 (car l)))
         (cons (mod (car l ) 10) 
               (createparameters 
                (cons (+ (cadr l) 1) 
                      (cddr l)))))
        (t (cons (car l) 
                 (createparameters (cdr l))))))

(defparameter test (list 11 11 2 3))
(setf test (createparameters test))
test ; ==> (1 2 3 3)

这是一个经过修改的变异版本(类似于您的代码):

(defun createparameters (l)
  (cond ((null l) l)
        ((< 9 (car l)) (setf (car l) (mod (car l ) 10))
                       (setf (cadr l) (+ (cadr l) 1))
                       (createparameters (cdr l)))
        (t (createparameters (cdr l)))))

(defparameter test (list 11 11 2 3))
(createparameters test)
test ; ==> (1 2 3 3)

我开始怀疑这是否是一个进位,因此第一个元素是最低有效数字,最后一个是最高数字。如果只是这样,只有添加一个只有在数字总是低于20时才能工作,如果最后一个数字变为10或更高,代码将不起作用。

答案 1 :(得分:1)

直接方法

这是一个直接执行任务的版本。

(defun decompose (digits)
  "Given a list of digits (least-significant digit first), return a
list of normalized digits, i.e., where each digit is less than 10."
  (do ((digits digits (rest digits))    ; iterate through the digits.
       ;; There's no initial digit, and the carry is initially 0.
       (carry 0) (digit)
       ;; The result starts as '(), and adds a digit on each successive
       ;; iteration, where the digit is computed in the loop body.
       (result '() (list* digit result)))
      ;; End when there are no digits left.  Most of the result is 
      ;; simply the reversed result, but if there's a non-zero carry
      ;; at the end, put it into a list and decompose it, too.
      ((endp digits)                    
       (nreconc result (if (zerop carry) '() 
                           (decompose (list carry))))) 
    ;; At each iteration, add the first digit to the carry, and divide
    ;; by 10.  The quotient is the carry for the next iteration, and 
    ;; the remainder is the digit that's added into the results.
    (multiple-value-setq (carry digit)
      (floor (+ carry (first digits)) 10))))
(decompose '(10005 2 3))
;=> (5 2 3 0 1)

(decompose '(11 2 4))
;=> (1 3 4)

(decompose '(23 0))
;=> (3 2)

(decompose '(11 11 4 6))
;=> (1 2 5 6)

模块化方法

更模块化的方法可能会将其分解为两部分。首先,给定一个数字列表,每个数字可能大于9,我们可以将原始数字重建为数字(即,不是数字列表)。这非常简单:

(defun digits->number (digits)
  (reduce (lambda (digit number)
            (+ digit (* 10 number)))
          digits
          :from-end t))
(digits->number '(1 2 3 4))
;=> 4321

(digits->number '(205 3))
;=> 235

现在,将数字转换为数字列表也不会太难。

(defun number->digits (number)
  (do ((digit)
       (digits '() (list* digit digits)))
      ((zerop number) (nreverse digits))
    (multiple-value-setq (number digit)
      (floor number 10))))
(number->digits 1024)
;=> (4 2 0 1)

(number->digits 8923)
;=> (3 2 9 8)

现在,我们可以观察digits->number将数字列表转换为我们需要的表单中的数字,即使存在&#39;数字&#39;大于9的number->digits始终生成一个所有数字都小于10的表示。因此,我们也可以将decompose实现为number->digits的{​​{1}}。

digits->number
(defun decompose (digits)
  (number->digits (digits->number digits)))

作为一个有趣的观察,我认为你可以说(decompose '(10005 2 3)) ;=> (5 2 3 0 1) (decompose '(11 2 4)) ;=> (1 3 4) (decompose '(23 0)) ;=> (3 2) (decompose '(11 11 4 6)) ;=> (1 2 5 6) 的输入空间是非负整数的列表,并且每个小于10的非负整数列表是修复的decompose的点