首先,我同意 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-duplicates/count-if 的能力,逻辑会变得有点复杂,因为您被迫使用基本功能。