reduce-tagged (was Re: toy list processing problem: collect similar terms)

Mirko mirko.vukovic at gmail.com
Mon Sep 27 11:40:45 EDT 2010


On Sep 27, 11:18 am, Mirko <mirko.vuko... at gmail.com> wrote:
> On Sep 26, 12:05 am, Xah Lee <xah... at gmail.com> wrote:
>
> I am hijacking the following post and driving it to Cuba (the Monthy
> Python fans will know what I refer to).  I want to create a `reduce'-
> like function that can handle similar problems.
>
> Xah said:
>
>
>
> > here's a interesting toy list processing problem.
>
> > I have a list of lists, where each sublist is labelled by
> > a number. I need to collect together the contents of all sublists
> > sharing
> > the same label. So if I have the list
>
> > ((0 a b) (1 c d) (2 e f) (3 g h) (1 i j) (2 k l) (4 m n) (2 o p) (4 q
> > r) (5 s t))
>
> > where the first element of each sublist is the label, I need to
> > produce:
>
> > output:
> > ((a b) (c d i j) (e f k l o p) (g h) (m n q r) (s t))
>
> > stuffed deleted.
>
> Here is my Common Lisp (and I only care about Common Lisp answers)
> attempt to create a `reduce'-like function to handle this kind of a
> problem (you will see that I am still struggling with the code and the
> documentation).
>
> (defun reduce-tagged (function sequence &key
>                  (key-tag #'first)
>                  (key-datum #'rest))
>   "Use a binary operation, `function' to combine a sequence of tagged
> elements.  like-tagged elements are `reduce'd according to `function'
> and returned in a list ...
>
> `sequence' is a sequence of tagged elements.  reduce-m will reduce
> like-tagged-elements.
>
> If `key-tag' is supplied it is used to extract the element tag.  If
> `key-tag' is not supplied, the function `first' is used.
>
> If `key-datum' is supplied, it is used to extract the element datum.
> If `key-datum' is not supplied, the function `rest' is used.
>
> "
>   (let ((hash (make-hash-table)))
>     (dolist (datum sequence)
>       (let ((tag (funcall key-tag datum))
>             (values (funcall key-datum datum)))
>         (multiple-value-bind (it present)
>             (gethash tag hash)
>           (if present
>               (setf (gethash tag hash)
>                     (apply function (gethash tag hash) values))
>               (setf (gethash tag hash) values)))))
>     (let (result)
>       (dohash (key value hash)
>         (push (list key value) result))
>       result)))
>
> Comments, improvements?  I am looking for a general purpose function
> like reduce that I
> can apply in various situations.
>
> Thanks,
>
> Mirko

Correction: the previous code used a non-portable clisp macro
`dohash' (looks nice, doesn't it?)

Here is the version with maphash:

(defun reduce-tagged (function sequence &key
		 (key-tag #'first)
		 (key-datum #'rest))
  "Use a binary operation, `function' to combine a sequence of tagged
elements.  like-tagged elements are `reduce'd according to `function'

`sequence' is a sequence of tagged elements.  reduce-m will reduce
like-tagged-elements.

If `key-tag' is supplied it is used to extract the element tag.  If
`key-tag' is not supplied, the function `first' is used.

If `key-datum' is supplied, it is used to extract the element datum.
If `key-datum' is not supplied, the function `rest' is used.

"
  (let ((hash (make-hash-table)))
    (dolist (datum sequence)
      (let ((tag (funcall key-tag datum))
	    (values (funcall key-datum datum)))
	(multiple-value-bind (it present)
	    (gethash tag hash)
	  (declare (ignore it))
	  (if present
	      (setf (gethash tag hash)
		    (apply function (gethash tag hash) values))
	      (setf (gethash tag hash) values)))))
    (let (result)
      (maphash #'(lambda(key value)
		   (push (list key value) result))
	       hash)
      result)))

Mirko



More information about the Python-list mailing list