这是一个任务:给定一个列表,某些元素也是列表。如果所有嵌套列表都是 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)
如何更改此代码以获得“完整”处理?
答案 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)))