【问题标题】:Decompose a list of numbers into digits将数字列表分解为数字
【发布时间】:2014-05-26 14:46:14
【问题描述】:

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

(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)))))

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

【问题讨论】:

  • '(11 11 4 6) 的结果是什么?
  • @uselpa 根据答案,我认为这可以被视为“计算多项式 a + 10b + 100c + 1000d + ...”,其中 a、b、c ... 是列表的元素,然后以相同的格式表示相同的值,但系数使得每个 a、b、c、... 都小于 10。”因此 11 + 11*10 + 4*100 + 6*1000 = 6521 = 1 + 2 *10 + 5*100 + 6*1000,所以系数为 (1 2 5 6)。

标签: lisp common-lisp


【解决方案1】:

您写道,如果第一个元素大于 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 或更高,则代码将不起作用。

【讨论】:

  • 递归检查还会修改我的列表吗?
  • @user3669610 问题是您没有在需要递归的地方显示任何测试,但如果说(createparameters '(11 11 1 2)) 需要变为(1 2 2 2),那么我可以更新我的答案以显示您如何使用并且没有突变。
  • 真的很有帮助
  • 谢谢西尔韦斯特!事实上,它是一个将被传播的进位。我正在开发一个程序,该程序将使用列表对大数进行算术运算,从而省略溢出。最好的问候!
  • @user3669610 你知道 CL fixnum 永远不会溢出吗?
【解决方案2】:

直接方法

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

(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-&gt;number 将数字列表转换为我们需要的形式的数字,即使存在大于 9 的“数字”。number-&gt;digits 总是产生一个表示,其中所有数字小于 10。因此,我们也可以将 decompose 实现为 number-&gt;digits of digits-&gt;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)

作为一个有趣的观察,我认为您可以说decompose 的输入空间是非负整数列表,并且每个小于 10 的非负整数列表是一个不动点 decompose

【讨论】:

    猜你喜欢
    • 2015-11-01
    • 1970-01-01
    • 2020-05-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多