From: Christian Nyb�
Subject: keywords to slot-names
Date: 
Message-ID: <873dh6t4pv.fsf@siteloft.no>
I'd like to get a list of structs that satisfy a filter.  This is the
code I've written:

(defparameter *message-stack* (make-hash-table :test '=) 
  "Hashtable with number SEQNO as key, struct MESSAGE as value.")

(defstruct message
  "Information about a message."
  (date :type time) 
  (topic :type string) 
  (page :type integer) 
  (initials :type string) 
  (revision :type fixnum)
  (distribution nil :type symbol)
  (status :type symbol))


(defmacro filter-structs (hash-containing-structs &rest args)
        "Return a list of keys of structs,
filtered by ARGS, which should be keys corresponding 
to slot-names of the structs, and the required values."
  `(let (list-of-seqnos)
     (maphash (lambda (key value)
		,(do ((first (first args) (first args))
		      (second (second args) (second args))
		      result)
		     ((not (and args (pop args) (pop args)))
		      `(and ,@result (push key list-of-seqnos)))
		   (push  `(equal 
			    ,second
			    (funcall 
			     (symbol-function 
			      (intern 
			       (concatenate 
				   'string 
				 (symbol-name (type-of value))
				 "-"
				 (symbol-name ,first))))
			     value))
			  result)))
	      ,hash-containing-structs)
     list-of-seqnos))

;;; Populate *message-stack*
(mapc (lambda (cons)
	(setf (gethash (car cons) *message-stack*) (cdr cons)))
      (list (cons 1 (make-message 
		     :date 14 :topic "topic" :page 2 
		     :initials "jqp" :revision 1 
		     :distribution nil :status 'pending))
	    (cons 2 (make-message
		     :date 12 :topic "some other topic" :page 2
		     :initials "jqp" :revision 3 
		     :distribution nil :status 'pending))
	    (cons 3 (make-message 
		     :date 13 :topic "the next topic" :page 2 
		     :initials "aph" :revision 1 
		     :distribution nil :status 'pending))))
;;; Usage
(filter-structs *message-stack* :initials "jqp" :revision 1)
;-> (1)
(filter-structs *message-stack* :initials "jqp")
;-> (2 1)
(filter-structs *message-stack* :status 'pending)
;-> (3 2 1)

The macro filter-structs looks somewhat hairy to me, particularly the
part where I deduce the slot-name from the type of the struct and the
keyword.  What do you think?
-- 
chr

From: Janis Dzerins
Subject: Re: keywords to slot-names
Date: 
Message-ID: <878zqxunkq.fsf@asaka.latnet.lv>
·····@eunet.no (Christian Nyb�) writes:

> I'd like to get a list of structs that satisfy a filter.  This is the
> code I've written:
> 
> (defparameter *message-stack* (make-hash-table :test '=) 
>   "Hashtable with number SEQNO as key, struct MESSAGE as value.")

Informative correction -- :test argument to make-hash-table is one of
eq, eql (default), equal or equalp.

Janis Dzerins
-- 
  Ever feel like life was a game and you had the wrong instruction book?
From: Christian Nyb�
Subject: Re: keywords to slot-names
Date: 
Message-ID: <87r94pqdw1.fsf@siteloft.no>
Janis Dzerins <·····@latnet.lv> writes:

> ·····@eunet.no (Christian Nyb�) writes:
> 
> > (defparameter *message-stack* (make-hash-table :test '=) 
> >   "Hashtable with number SEQNO as key, struct MESSAGE as value.")
> 
> Informative correction -- :test argument to make-hash-table is one of
> eq, eql (default), equal or equalp.

Although CLTL2 does not explicitly permit other designators, the
implementation I use (ACL 5.01) allows for any test function.  Using
`=' instead of `eq' tells readers that I want the hash to test
numbers, rather than objects.  That's why I used it.

I think
#+Allegro (make-hash-table :test '=) #-Allegro(make-hash-table :test 'eq) 
would be better, as it would have conveyed the same information in a
portable manner.
-- 
chr
From: Christophe Rhodes
Subject: Re: keywords to slot-names
Date: 
Message-ID: <sq4s1lawvj.fsf@lambda.jesus.cam.ac.uk>
·····@eunet.no (Christian Nyb�) writes:

> Although CLTL2 does not explicitly permit other designators, the
> implementation I use (ACL 5.01) allows for any test function.  Using
> `=' instead of `eq' tells readers that I want the hash to test
> numbers, rather than objects.  That's why I used it.
> 
> I think
> #+Allegro (make-hash-table :test '=) #-Allegro(make-hash-table :test 'eq) 
> would be better, as it would have conveyed the same information in a
> portable manner.

I don't think that eq is a good idea if you're using numbers. 
(eq 3.0 3.0) is not guaranteed to return t.

From the Hyperspec, on function EQ:

"An implementation is permitted to make ``copies'' of characters and
numbers at any time. The effect is that Common Lisp makes no guarantee
that eq is true even when both its arguments are ``the same thing'' if
that thing is a character or number."

Christophe
-- 
Jesus College, Cambridge, CB5 8BL                           +44 1223 524 842
(FORMAT T "(·@{~w ········@{~w~^ ~})" 'FORMAT T "(·@{~w ········@{~w~^ ~})")
From: Kent M Pitman
Subject: Re: keywords to slot-names
Date: 
Message-ID: <sfwzojdm1un.fsf@world.std.com>
Christophe Rhodes <·····@cam.ac.uk> writes:

> ·····@eunet.no (Christian Nyb�) writes:
> 
> > Although CLTL2 does not explicitly permit other designators, the
> > implementation I use (ACL 5.01) allows for any test function.  Using
> > `=' instead of `eq' tells readers that I want the hash to test
> > numbers, rather than objects.  That's why I used it.
> > 
> > I think
> > #+Allegro (make-hash-table :test '=) #-Allegro(make-hash-table :test 'eq) 
> > would be better, as it would have conveyed the same information in a
> > portable manner.
> 
> I don't think that eq is a good idea if you're using numbers. 
> (eq 3.0 3.0) is not guaranteed to return t.

equalp is a bertter test to use if you're using numbers since it's portable
and it bottoms out at = for numbers.
From: Marco Antoniotti
Subject: Re: keywords to slot-names
Date: 
Message-ID: <y6cvgtz23ry.fsf@octagon.mrl.nyu.edu>
·····@eunet.no (Christian Nyb�) writes:

> Janis Dzerins <·····@latnet.lv> writes:
> 
> > ·····@eunet.no (Christian Nyb�) writes:
> > 
> > > (defparameter *message-stack* (make-hash-table :test '=) 
> > >   "Hashtable with number SEQNO as key, struct MESSAGE as value.")
> > 
> > Informative correction -- :test argument to make-hash-table is one of
> > eq, eql (default), equal or equalp.
> 
> Although CLTL2 does not explicitly permit other designators, the
> implementation I use (ACL 5.01) allows for any test function.  Using
> `=' instead of `eq' tells readers that I want the hash to test
> numbers, rather than objects.  That's why I used it.
> 
> I think
> #+Allegro (make-hash-table :test '=) #-Allegro(make-hash-table :test 'eq) 
> would be better, as it would have conveyed the same information in a
> portable manner.

And 

	(make-hash-table :test #'eql)

would be portable across the board without any problem and without
#+/#-.

Cheers

-- 
Marco Antoniotti =============================================================
NYU Bioinformatics Group			 tel. +1 - 212 - 998 3488
719 Broadway 12th Floor                          fax  +1 - 212 - 995 4122
New York, NY 10003, USA				 http://galt.mrl.nyu.edu/valis
             Like DNA, such a language [Lisp] does not go out of style.
			      Paul Graham, ANSI Common Lisp
From: Kent M Pitman
Subject: Re: keywords to slot-names
Date: 
Message-ID: <sfw1ywn22cd.fsf@world.std.com>
Marco Antoniotti <·······@cs.nyu.edu> writes:

> And 
> 
> 	(make-hash-table :test #'eql)
> 
> would be portable across the board without any problem and without
> #+/#-.

It depends on whether he wants 3.0 and 3 to be seen as equivalent hash 
keys.  The fnction = will do this since (= 3.0 3), but EQL will not since 
(NOT (EQL 3.0 3)).  However, (EQUALP 3.0 3), which is why I suggested an
EQUALP hash table.
From: Marco Antoniotti
Subject: Re: keywords to slot-names
Date: 
Message-ID: <y6caebb62fw.fsf@octagon.mrl.nyu.edu>
Kent M Pitman <······@world.std.com> writes:

> Marco Antoniotti <·······@cs.nyu.edu> writes:
> 
> > And 
> > 
> > 	(make-hash-table :test #'eql)
> > 
> > would be portable across the board without any problem and without
> > #+/#-.
> 
> It depends on whether he wants 3.0 and 3 to be seen as equivalent hash 
> keys.  The fnction = will do this since (= 3.0 3), but EQL will not since 
> (NOT (EQL 3.0 3)).  However, (EQUALP 3.0 3), which is why I suggested an
> EQUALP hash table.

I stand corrected.

Cheers

-- 
Marco Antoniotti =============================================================
NYU Bioinformatics Group			 tel. +1 - 212 - 998 3488
719 Broadway 12th Floor                          fax  +1 - 212 - 995 4122
New York, NY 10003, USA				 http://galt.mrl.nyu.edu/valis
             Like DNA, such a language [Lisp] does not go out of style.
			      Paul Graham, ANSI Common Lisp
From: Kent M Pitman
Subject: Re: keywords to slot-names
Date: 
Message-ID: <sfwk8ait004.fsf@world.std.com>
·····@eunet.no (Christian Nyb�) writes:

> I'd like to get a list of structs that satisfy a filter.  This is the
> code I've written:
> 
> (defmacro filter-structs (hash-containing-structs &rest args)
>         "Return a list of keys of structs,
> filtered by ARGS, which should be keys corresponding 
> to slot-names of the structs, and the required values."
>   `(let (list-of-seqnos)
>      (maphash (lambda (key value)
> 		,(do ((first (first args) (first args))
> 		      (second (second args) (second args))
> 		      result)
> 		     ((not (and args (pop args) (pop args)))
> 		      `(and ,@result (push key list-of-seqnos)))
> 		   (push  `(equal 
> 			    ,second
> 			    (funcall 
> 			     (symbol-function 
> 			      (intern 
> 			       (concatenate 
> 				   'string 
> 				 (symbol-name (type-of value))
> 				 "-"
> 				 (symbol-name ,first))))
> 			     value))
> 			  result)))
> 	      ,hash-containing-structs)
>      list-of-seqnos))

First, this is not robust because it's not interning the symbol in any
particular package.  You have no idea what package will prevail when you
are running this code--it might not be the load package even.  One very
explicit reason DEFCLASS doesn't do the :conc-name thing that DEFSTRUCT 
does is that we had a lot of problems in the olden days with code like this.
Even if you make INTERN use a fixed package, like (intern ... *my-package*),
you may sometimes lose. e.g., if the type-of was in another package that was
not *my-package*, the constructors *probably* [but not necessarily] will
be, too.  Ditto if you do (intern ... (symbol-package (type-of value)))
you will lose in some cases.  The reason  you'll sometimes lose has to do
with package use list searching, so that if you have a struct named CAR,
you can't do 
  (intern (symbol-name (type-of *my-car*)) (symbol-package (type-of *my-car*)))
because you'll sometimes find CAR is in the CL package, and it's bad to
intern other symbols like CAR-ENGINE and MAKE-CAR in that package. That may
be a bad example, but at least it gives you the flavor of how use lists 
interfere.  It's possible to construct many other examples.  
Symbol magic at runtime is also pretty slow--you don't want to be doing
that many interns if you're searching a thousand-message db on a busy 
machine.

You are better off using classes and directly using SLOT-VALUE, for example.
So you'd say (filter-strucsts *message-stack* 'initials "jqp" 'revision 1)
and then you'd use (slot-value message slot-name) in order to get the value.
Slot-value is both less error-prone (because changes to the slot-name and
its package happen in predictable ways between definition and use) and more
efficient (because slot-value is NOT slow).