LISP

时间:2018-10-28 14:34:12

标签: list recursion functional-programming lisp

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

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

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

3 个答案:

答案 0 :(得分:0)

这绝对不是最好的解决方案,但它可以起作用:

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

答案 1 :(得分:0)

允许我根据自己的回答显示一些改进。

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

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

第三个函数不是 test ,而是 sum 。请注意,由于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)

答案 2 :(得分:0)

我认为这是一个满足问题要求的解决方案:递归求和一个列表,每个列表的元素可以是偶数或满足相同要求的列表。它也仅对要求和的结构进行一次传递。对于大型列表,它依赖于实现中的尾部调用消除,这在现在可能始终是正确的,但并非必须如此。如果没有,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)))