【问题标题】:Recursive processing of list elements in LISPLISP中列表元素的递归处理
【发布时间】:2019-04-01 14:59:40
【问题描述】:

这是一个任务:给定一个列表,其中一些元素也是列表。如果所有嵌套列表都是偶数,则需要使用recursion将嵌套列表替换为其中的数字之和。例如:

(1 2 NIL (2 4 6) 5 7) -> (1 2 NIL 12 5 7) 

如果父列表匹配转换后的条件:

(2 2 (4 4) (2 2)) -> (2 2 8 4) -> 16

现在我有以下代码:

;; check  for all list elements are even 
(defun is-even-list (lst)
    (cond ((null lst) t)
        ((and (numberp (car lst)) (evenp (car lst))) (is-even-list (cdr lst)))      
        (t nil)
    )
)

;; list summing 
(defun sum-list (lst)
    (cond ((null lst) 0)
        (t (+ (car lst) (sum-list (cdr lst))))
    )
) 

;; main func 
(defun task (lst)
    (cond ((null lst) nil)
        ((atom (car lst)) (cons (car lst) (task (cdr lst))))
        ((is-even-list (car lst)) (cons (list (sum-list (car lst))) (task (cdr lst))))
        (t (cons (task (car lst)) (task (cdr lst))))
    )
)

但现在它只处理列表的“最低”级别(如果存在):

(2 4)               -> (2 4)
(2 (2 4 6) 6)       -> (2 12 6)
(2 (4 (6 8) 10) 12) -> (2 (4 14 10) 12)
(2 (4 6) (8 10) 12) -> (2 10 18 12)

如何更改此代码以获得“完整”处理?

【问题讨论】:

  • 不,按照逻辑上的规范,它应该是(2 2 (4 4) (2 2)) -> (2 2 8 4) -> 16。你有它的方式,它保证在处理后 not 匹配条件:(8) 不是偶数。它甚至不是一个数字。
  • 对不起,我用括号指向求和结果,并不是说结果必须是单元素列表。我删除了它们。
  • 好的,仍然必须是(2 2 (4 4) (2 2)) -> (2 2 8 4) -> 16,而不是(16)

标签: list recursion functional-programming lisp


【解决方案1】:

这绝对不是最好的解决方案,但它确实有效:

(defun is-even-list (lst)
    (cond ((null lst) t)
        ((and (numberp (car lst)) (evenp (car lst))) (is-even-list (cdr lst)))      
        (t nil)
    )
)

(defun sum-list (lst)
    (cond ((null lst) 0)
        (t (+ (car lst) (sum-list (cdr lst))))
    )
) 

(defun test (lst)   
    (dotimes (i (list-length lst))      
        (cond           
            ((not (atom (nth i lst))) (setf (nth i lst) (test (nth i lst))))
        )
    )

    (cond       
        ((is-even-list lst) (setf lst (sum-list lst)))
        ((not (is-even-list lst)) (setf lst lst))       
    )   
)

【讨论】:

    【解决方案2】:

    请允许我对您自己的答案进行一些改进。

    首先,使用常规格式:没有悬空括号,正文缩进两个空格,其他参数形式对齐。使用适当的换行符。

    (defun is-even-list (lst)
      (cond ((null lst) t)
            ((and (numberp (car lst))
                  (evenp (car lst)))
             (is-even-list (cdr lst)))            
            (t nil)))
    
    (defun sum-list (lst)
      (cond ((null lst) 0)
            (t (+ (car lst)
                  (sum-list (cdr lst))))))
    
    (defun test (lst)
      (dotimes (i (list-length lst))
        (cond ((not (atom (nth i lst)))
               (setf (nth i lst) (test (nth i lst))))))
      (cond ((is-even-list lst) (setf lst (sum-list lst)))
            ((not (is-even-list lst)) (setf lst lst))))
    

    第一个函数检查两件事:每个元素都是一个数字,并且每个元素都是偶数。在这种情况下,第一个条件主要是指:没有子列表。

    (defun flat-all-even-p (list)
      (and (every #'numberp list)
           (every #'even list)))
    

    第二个函数对列表求和,并假设所有元素都是数字(子列表会在此处表示错误)。

    (defun sum (list)
      (reduce #'+ list))
    

    第三个函数不测试,它求和。请注意,它只是意外返回答案,因为setf 返回它设置的值。另一个问题是您在循环中对列表进行索引查找,这是非常低效的。最后,你修改了给你的列表,这会让你的调用者感到惊讶。

    (defun sum-if-all-even (tree)
      (if (listp tree)
          (let ((recursed-tree (mapcar #'sum-if-all-even tree)))
            (if (flat-all-even-p recursed-tree)
                (sum recursed-tree)
                recursed-tree))
          tree)
    

    【讨论】:

      【解决方案3】:

      这是一个我认为符合问题要求的解决方案:递归求和一个列表,其中每个元素是偶数或满足相同要求的列表。它也这样做了,它只对它试图求和的结构进行一次传递。对于大型列表,它依赖于实现中的尾调用消除,这现在可能总是正确的,但不是必须的。如果没有,sum-list-loop 可以变成明确迭代的东西。

      (defun sum-list-if-even (l)
        ;; Sum a list if all its elements are either even numbers or lists
        ;; for which this function returns an even number.  If that's not
        ;; true return the list.  This assumes that the list is proper and
        ;; elements are numbers or lists which meet the same requirement but
        ;; it does not check this in cases where it gives up for other
        ;; reasons first: (sum-list-if-even '(2 "")) signals a type error
        ;; (but (sum-list-if-even '(1 "")) fails to do so)
        (labels ((sum-list-loop (tail sum)
                   (etypecase tail
                     (null sum)               ;all the elements of '() are even numbers
                     (cons
                      (let ((first (first tail)))
                        (etypecase first
                          (integer
                           ;; Easy case: an integer is either an even number
                           ;; or we give up immediately
                           (if (evenp first)
                               (sum-list-loop (rest tail) (+ sum first))
                             ;; give up immediately
                             l))
                          (list
                           ;; rerurse on the car ...
                           (let ((try (sum-list-if-even first)))
                             ;; ... and check to see what we got to know if
                             ;; we should recurse on the cdr
                             (if (not (eq try first))
                                 (sum-list-loop (rest tail) (+ sum try))
                               l)))))))))
          (sum-list-loop l 0)))
      

      【讨论】:

      • 不应该(sum-list-if-even '(1 (2 4) 6))返回(1 6 6)吗?我得到(1 (2 4) 6)
      • @Knuto:它应该根据其规范返回(1 (2 4) 6):列表不是所有元素都是偶数或满足相同要求的列表。这可能不是问题所要求的,但这就是我阅读它的方式以及该功能的设计目的。
      猜你喜欢
      • 2016-06-28
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-03-24
      • 1970-01-01
      • 1970-01-01
      • 2017-01-13
      • 1970-01-01
      相关资源
      最近更新 更多