From: Scott Burson on
On Dec 22, 2:41 pm, rkk <rajesh.ko...(a)gmail.com> wrote:
> I have an application where I create and store triangles, a triangle
> being a set of 3 points,
> (p0 p1 p2).

I strongly recommend you take a look at FSet, my functional set-
theoretic collections library for Common Lisp:

http://common-lisp.net/project/fset/

It makes the kind of thing you're trying to do very easy, as it has
many built-in set operations including equality testing, and maps can
be keyed by sets with no special effort.

-- Scott
From: Willem Broekema on
On Dec 22, 11:41 pm, rkk <rajesh.ko...(a)gmail.com> wrote:
> I know the standard does not allow this, but has anyone implemented
> custom hash tables where this can be done?

Many implementations allow this. You need to define both an equality
and a hash function, and they must be consistent (two objects equal =>
their hashes identical).

This is what I use to implement Python hash tables in 5 common
implementations:

;;; Python dicts are hash tables with custom equality (py-==) and hash
functions (py-hash).

#+sbcl
(sb-ext:define-hash-table-test py-==->lisp-val py-hash)
#+cmu
(extensions:define-hash-table-test 'py-hash-table-test #'py-==->lisp-
val #'py-hash)

(defun make-py-hash-table ()
(or #+(or allegro ccl lispworks) (make-hash-table :test 'py-==->lisp-
val :hash-function 'py-hash)
#+cmu (make-hash-table :test 'py-hash-table-test)
#+sbcl (make-hash-table :test 'py-==->lisp-val)
#-(or allegro ccl lispworks cmu sbcl) (error "Creating python
dict not suported in this environment.")))
From: Joshua Taylor on
rkk wrote:
> I have an application where I create and store triangles, a triangle
> being a set of 3 points,
> (p0 p1 p2). The triangles are created and stored using something along
> the lines of
>
> (defun make-triangle (p0 p1 p2)
> (setf (get-hash (get-next-id) *TRIANGLES*) (list p0 p1 p2)))
>
> where (get-next-id) returns a unique id. *TRIANGLES* is defined as
>
> (defvar *TRIANGLES* (make-hash-table))
>
> Two triangles are "equal" if they have the same points. The order of
> the points is not important,
[...]
> I know the standard does not allow this, but has anyone implemented
> custom hash tables where this can be done?
>
> Thanks in advance
> rkk

Willem Boekema responded with a way to do this with hash tables
directly, as many implementations do have extensions for hash-tables.

Without adding reader conditionals in lots of places though, you can
portably do what you're doing by putting your points in some canonical
order (since you said that the order doesn't matter), and hashing with a
test that can compare the list of points. Of course, how you represent
your points will determine how easily this can be done. Some
representations might require interning your points too. For instance,
let's assume points are conses each of whose CAR and CDR is a real.
We'll sort points lexicographically (i.e., sort by CAR, and when CARs
are the same, sort by CDR).


(defun point< (p1 p2)
(cond
((< (car p1) (car p2)) t)
((> (car p1) (car p2)) nil)
((< (cdr p1) (cdr p2)))))

(defvar *polygons*
(make-hash-table :test 'equal))

(defun intern-polygon (&rest points)
(let ((points (sort points 'point<)))
(multiple-value-bind (polygon presentp)
(gethash points *polygons*)
(if presentp polygon
(setf (gethash points *polygons*) points)))))


Since we hashed on a list of points, it's easier to take an arbitrary
number of points as an &rest list, but a variant for exactly three
points wouldn't be hard. Here, interned polygons are EQ comparable,
which might save you the need for those unique ids too, and you don't
need to be mapping back and forth between polygons and ids.


(let ((p1 (intern-polygon '(3 . 8) '(2 . 3) '(1 . 3)))
(p2 (intern-polygon '(2 . 3) '(1 . 3) '(3 . 8)))
(p3 (intern-polygon '(2 . 3) '(1 . 3) '(3 . 8)))
(p4 (intern-polygon '(3 . 4) '(9 . 3) '(0 . 3))))
(list (eq p1 p2)
(eq p1 p3)
(eq p1 p4)))
;=> (T T NIL)


//JT
From: Gene on
On Dec 22, 5:41 pm, rkk <rajesh.ko...(a)gmail.com> wrote:
> I have an application where I create and store triangles, a triangle
> being a set of 3 points,
> (p0 p1 p2). The triangles are created and stored using something along
> the lines of
>
> (defun make-triangle (p0 p1 p2)
>   (setf (get-hash (get-next-id) *TRIANGLES*) (list p0 p1 p2)))
>
> where (get-next-id) returns a unique id. *TRIANGLES* is defined as
>
> (defvar *TRIANGLES* (make-hash-table))
>
> Two triangles are "equal" if they have the same points. The order of
> the points is not important,
> and hence I defined the following:
>
> (defun set-equal? (set1 set2)
>   (and (eql (set-difference set1 set2) nil) (eql (set-difference set2
> set1) nil)))
>
> Two triangles share an edge / are said to be connected if (= 2 (length
> (intersection tr1 tr2))) etc.
>
> In my application, I cannot have duplicate triangles, so right now,
> before I call make-triangle, I check
> if *TRIANGLES* already has an existing triangle with the same points
> by walking through the hashtable.
>
> Currently my (key,value) corresponds to (id, (p0,p1,p2)). My question
> is can I invert this and instead have
> (key,value) correspond to ((p0,p1,p2), id). To do this, I would need
> to have something like
>
> (defvar *TRIANGLES* (make-hash-table :test #'set-equal?))
>
> I know the standard does not allow this, but has anyone implemented
> custom hash tables where this can be done?

One note if performance is any kind of issue: If you give points
unique integer ids, then any triangle can be easily be placed in a
canonical form: with the points in (say) ascending id order. This
simplifies equality and common edge tests. A list of the 3 sorted
integer ids makes a nice hash key, so a hash table with #'equal
predicate implements content-addressable memory.