我的任务是计算列表中的所有元素,这些元素具有重复项,例如
(2 2(3 3)4(3))将导致2(因为只有2和3有重复)
Searchdeep - 如果在列表WHAT
中找不到WHERE
,则只返回nill
Count2 - 浏览单个元素和子列表
如果它找到原子,他将使用SEARCHDEEP
来确定它是否有重复,那么将检查列表OUT
(以确定该原子是否尚未计算(例如(3) 3),应该返回1,而不是2)
,增加计数器并将原子添加到OUT
列表中。
然而,我不明白为什么,但它不断只返回1.我认为这是某种逻辑错误或错误使用功能。
我的代码是:
(SETQ OUT NIL)
(SETQ X (LIST 2 -3 (LIST 4 3 0 2) (LIST 4 -4) (LIST 2 (LIST 2 0 2))-5))
(SETQ count 0)
(DEFUN SEARCHDEEP (WHAT WHERE) (COND
((NULL WHERE) NIL)
(T (OR
(COND
((ATOM (CAR WHERE)) (EQUAL WHAT (CAR WHERE)))
(T (SEARCHDEEP WHAT (CAR WHERE)))
)
(SEARCHDEEP WHAT (CDR WHERE))
)
)
)
)
(DEFUN Count2 ( input)
(print input)
(COND
((NULL input) NIL)
(T
(or
(COND
((ATOM (CAR input))
(COND
(
(and ;if
(SEARCHDEEP (CAR INPUT) (CDR INPUT))
(NOT (SEARCHDEEP (CAR INPUT) OUT))
)
(and ;do
(Setq Count (+ count 1))
(SETQ OUT (append OUT (LIST (CAR INPUT))))
(Count2 (CDR input))
)
)
(t (Count2 (CDR input)))
)
)
(T (Count2 (CAR input)))
)
(Count2 (CDR input))
)
)
)
)
(Count2 x)
(print count)
答案 0 :(得分:3)
首先,您的代码存在一些大的样式问题。不要用大写字写(有些像我一样,喜欢在评论和代码之外的文本中用大写字母写代码,但代码本身应该用小写字母写),不要在括号上加上括号自己的路线。因此SEARCHDEEP
函数看起来应该更像
(defun search-deep (what where)
(cond ((null where) nil)
(t (or (cond ((atom (car where)) (equal what (car where)))
(t (searchdeep what (car where))))
(searchdeep what (cdr where))))))
您也不应该使用SETQ
来定义变量。请改用DEFPARAMETER
或DEFVAR
,但在这种情况下,您不应该首先使用全局变量。您应该在名称周围使用星号命名全局变量(*X*
而不是x
,但使用更具描述性的名称。)
对于问题本身,我将首先编写一个遍历树的函数。
(defun traverse-tree (function tree)
"Traverse TREE, calling FUNCTION on every atom."
(typecase tree
(atom (funcall function tree))
(list (dolist (item tree)
(traverse-tree function item))))
(values))
请注意,TYPECASE
在这种情况下比COND
更具可读性。您还应该使用语言提供的映射或循环结构,而不是自己编写递归循环。最后的(values)
表示该函数不会返回任何内容。
(let ((tree '(2 -3 (4 3 0 2) (4 -4) (2 (2 0 2)) -5)))
(traverse-tree (lambda (item)
(format t "~a " item))
tree))
; 2 -3 4 3 0 2 4 -4 2 2 0 2 -5
; No values
如果你经常穿越树木,你可以隐藏DO-TREE
宏背后的那个功能
(defmacro do-tree ((var tree &optional result) &body body)
`(progn (traverse-tree (lambda (,var)
,@body)
,tree)
,result))
(let ((tree '(2 -3 (4 3 0 2) (4 -4) (2 (2 0 2)) -5)))
(do-tree (item tree)
(format t "~a " item)))
; 2 -3 4 3 0 2 4 -4 2 2 0 2 -5
;=> NIL
使用它,我们可以编写一个函数来计算树中的每个元素,返回一个alist。我将使用哈希表来跟踪计数。如果您只对计算将保持在较小范围内的数字感兴趣,则可能需要使用向量。
(defun tree-count-elements (tree &key (test 'eql))
"Count each item in TREE. Returns an alist in
form ((item1 . count1) ... (itemn . countn))"
(let ((table (make-hash-table :test test)))
(do-tree (item tree)
(incf (gethash item table 0)))
(loop for value being the hash-value in table using (hash-key key)
collect (cons key value))))
(let ((tree '(2 -3 (4 3 0 2) (4 -4) (2 (2 0 2)) -5)))
(tree-count-elements tree))
;=> ((2 . 5) (-3 . 1) (4 . 2) (3 . 1) (0 . 2) (-4 . 1) (-5 . 1))
该函数采用TEST
的关键字参数与哈希表一起使用。对于数字或字符,EQL
有效。
现在,您可以使用标准COUNT-IF
- 函数来计算多次出现的元素。
(let ((tree '(2 -3 (4 3 0 2) (4 -4) (2 (2 0 2)) -5)))
(count-if (lambda (item)
(> item 1))
(tree-count-elements tree)
:key #'cdr))
;=> 3