LISP - 计算列表中每个值的出现次数

时间:2012-08-30 10:53:31

标签: lisp common-lisp unique duplicate-removal

我为糟糕的英语道歉..
我有一个任务是编写一个名为“make-bag”的函数来计算列表中每个值的出现次数 并返回一个点对的列表,如下所示:'((value1.num-occurences1)(value2.num-occurences2)...) 例如:

 (make-bag '(d c a b b c a))
((d . 1) (c . 2) (a . 2) (b . 2))

(列表不必排序)

我们的讲师允许我们使用函数MAPCARFILTER(假设它已实现),  但我们不允许使用REMOVE-DUPLICATESCOUNT-IF。 他还要求我们使用递归。

有没有办法只计算一次每个值而不删除重复项? 如果有办法,可以通过递归来完成吗?

2 个答案:

答案 0 :(得分:2)

首先,我同意Joswig先生的意见 - Stackoverflow不是一个要求家庭作业答案的地方。但是,我将以一种方式回答您的问题,即如果没有额外的挖掘并且能够理解散列表和词法闭包是如何工作的,您可能无法直接使用它。这对你的进步来说是一个很好的锻炼。

  

有没有办法只计算一次每个值而不删除重复项?如果有办法,可以通过递归来完成吗?

是的,它直接使用哈希表,这里有两个例子:

;; no state stored
(defun make-bag (lst)
  (let ((hs (make-hash-table)))
    (labels ((%make-bag (lst)
               (if lst
                   (multiple-value-bind (val exists)
                       (gethash (car lst) hs)
                     (if exists
                         (setf (gethash (car lst) hs) (1+ val))
                         (setf (gethash (car lst) hs) 1))
                     (%make-bag (cdr lst)))
                   hs)))
      (%make-bag lst))))

现在,如果您尝试两次评估此表单,则每次都会得到相同的答案:

(gethash 'a (make-bag '(a a a a b b b c c b a 1 2 2 1 3 3 4 5 55)))
> 5
> T

(gethash 'a (make-bag '(a a a a b b b c c b a 1 2 2 1 3 3 4 5 55)))
> 5
> T

这是第二个例子:

;; state is stored....
(let ((hs (make-hash-table)))
  (defun make-bag (lst)
    (if lst
        (multiple-value-bind (val exists)
            (gethash (car lst) hs)
          (if exists
              (setf (gethash (car lst) hs) (1+ val))
              (setf (gethash (car lst) hs) 1))
          (make-bag (cdr lst)))
        hs)))

现在,如果您尝试两次评估此表单,您将第二次获得双倍答案:

(gethash 'x (make-bag '(x x x y y x z z z z x)))
> 5
> T

(gethash 'x (make-bag '(x x x y y x z z z z x)))
> 10
> T

为什么答案加倍?

如何将哈希表的内容转换为关联列表?

另请注意,递归函数通常会“吃掉”列表,有时会有一个累加器累积每个步骤的结果,最后会返回。没有散​​列表和使用remove-duplicatelicates / count-if的能力,由于你被迫使用基本函数,逻辑会有点复杂。

答案 1 :(得分:1)

嗯,这就是答案,但是为了使它更有用,作为一种学习练习,我将留下一些空白,你必须填补。

另请注意,对此任务使用哈希表会更有利,因为存储在哈希表中的元素的访问时间是固定的(通常非常小),而对存储在列表中的元素的访问时间具有线性复杂度,因此会随着更长的列表而增长。

(defun make-bag (list)
  (let (result)
    (labels ((%make-bag (list)
               (when list
                 (let ((key (assoc (car <??>) <??>)))
                   (if key (incf (cdr key))
                       (setq <??>
                             (cons (cons (car <??>) 1) <??>)))
                   (%make-bag (cdr <??>))))))
      (%make-bag list))
    result))

此功能可能有所不同,但它们大致基于相同的原则。