Sysop: | Amessyroom |
---|---|
Location: | Fayetteville, NC |
Users: | 40 |
Nodes: | 6 (0 / 6) |
Uptime: | 10:43:01 |
Calls: | 291 |
Files: | 910 |
Messages: | 76,430 |
Suppose I have:
(defvar *list*
'((1 2)
(3 4)
(5 6)
(1 7)))
Now, suppose I want the "keys" of the list, defined by the first element
of the list. Is there a Lisp function which is callable something like: (keys *list* :key #'first) ; => '(1 3 5)
Suppose further that I want to accumulate totals based on keys. The
first element in the list is the key, and the second element in the list
is the value. Is there a Lisp function which is callable something like: (accum *list*) ; => '( (1 9) (3 4) (5 6) )
What we can do is define a somewhat general function for processing this type of associative list.
(defun histogram (assoc-list reduce-func &rest reduce-args)
(let ((hash (make-hash-table :test #'eql)))
(loop for (key value) in assoc-list
do (push value (gethash key hash)))
(loop for key being the hash-keys of hash
using (hash-value value-list)
collect `(,key
,(apply #'reduce reduce-func value-list reduce-args)))))
What HISTOGRAM does is collates the values that share the same key into lists, and then it processes each list through REDUCE, so that you can summarize the values using arbitrary arithmetic, not only addition.
Some tests:
Add:
(histogram '((1 2) (3 4) (5 6) (1 7)) #'+)
((5 6) (3 4) (1 9))
Multiply:
(histogram '((1 2) (3 4) (5 6) (1 7)) #'*)
((5 6) (3 4) (1 14))
Add, supplying initial value for each summation:
(histogram '((1 2) (3 4) (5 6) (1 7)) #'+ :initial-value 100)
((5 106) (3 104) (1 109))