常见的lisp工作与列表

时间:2016-12-18 17:37:56

标签: functional-programming common-lisp

我的任务是计算列表中的所有元素,这些元素具有重复项,例如 (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)

1 个答案:

答案 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来定义变量。请改用DEFPARAMETERDEFVAR,但在这种情况下,您不应该首先使用全局变量。您应该在名称周围使用星号命名全局变量(*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