From: M Jared Finder
Subject: How to pass on only unrecognized keyword params?
Date: 
Message-ID: <4233c9fa$1_2@x-privat.org>
I'm writing a small helper function, MAKE-ELT-HASH-TABLE, which creates
a hash table that maps objects to their indexes, so I don't have to call
POSITION in a loop.  Ideally, I'd like this function to act just like
MAKE-HASH-TABLE, but take two extra parameters, a sequence, and a
keyword parameter for a filter to be applied.

The only way I can think of to pass on only the unrecognized keyword
parameters to MAKE-HASH-TABLE is to create a set of huge, nested,
conditions like this:

> (defun make-elt-hash-table (seq &key (key #'identity) (test #'eql)
>                                      (size 0 size-specified?) (rehash-size 0 rehash-size-specified?)
>                                      (rehash-threshold 0 rehash-threshold-specified?))
>   "Create a hash table that maps from (KEY <object-in-seq>) to indexes that can get passed to ELT."
>   (flet ((make-hash-table-params ()
>            (if size-specified?
>                (if rehash-size-specified?
>                    (if rehash-threshold-specified?
>                        (make-hash-table :test test :size size :rehash-size rehash-size 
>                                         :rehash-threshold rehash-threshold)
>                        (make-hash-table :test test :size size :rehash-size rehash-size))
>                    (if rehash-threshold-specified?
>                        (make-hash-table :test test :size size :rehash-threshold rehash-threshold)
>                        (make-hash-table :test test :size size)))
>                (if rehash-size-specified?
>                    (if rehash-threshold-specified?
>                        (make-hash-table :test test :rehash-size rehash-size 
>                                         :rehash-threshold rehash-threshold)
>                        (make-hash-table :test test :rehash-size rehash-size))
>                    (if rehash-threshold-specified?
>                        (make-hash-table :test test :rehash-threshold rehash-threshold)
>                        (make-hash-table :test test))))))
>     (let ((accum (make-hash-table-params))
>           (index 0))
>       (doseq (elem seq)
>         (setf (gethash (funcall key elem) accum) index)
>         (incf index))
>       accum)))

There must be a better way; I just can't think of it.  How could I do 
this in a clean way?

   -- MJF

From: Wade Humeniuk
Subject: Re: How to pass on only unrecognized keyword params?
Date: 
Message-ID: <BtQYd.34476$gJ3.25270@clgrps13>
(defun make-elt-hash-table (seq &rest rest &key (key #'identity) &allow-other-keys)
   "Create a hash table that maps from (KEY <object-in-seq>) to indexes that can get 
passed to ELT."
   (let* ((keyword-args (copy-list rest)))
     (remf keyword-args :key)
     (let ((ht (apply #'make-hash-table keyword-args)) (index 0))
       (map nil
            (lambda (elt)
              (setf (gethash (funcall key elt) ht) index)
              (incf index))
            seq)
       ht)))

CL-USER 1 > (make-elt-hash-table '(a d r f e) :test #'eql :size 10)
#<EQL Hash Table{5} 206AB8D4>

CL-USER 2 > (setf ht *)
#<EQL Hash Table{5} 206AB8D4>

CL-USER 3 > (gethash 'd ht)
1
T

CL-USER 4 >

Wade
From: M Jared Finder
Subject: Re: How to pass on only unrecognized keyword params?
Date: 
Message-ID: <4233e32c_4@x-privat.org>
Wade Humeniuk wrote:
> (defun make-elt-hash-table (seq &rest rest &key (key #'identity) 
> &allow-other-keys)
>   "Create a hash table that maps from (KEY <object-in-seq>) to indexes 
> that can get passed to ELT."
>   (let* ((keyword-args (copy-list rest)))
>     (remf keyword-args :key)
>     (let ((ht (apply #'make-hash-table keyword-args)) (index 0))
>       (map nil
>            (lambda (elt)
>              (setf (gethash (funcall key elt) ht) index)
>              (incf index))
>            seq)
>       ht)))

Thank you very much; I did not know about the macro REMF.  That's so 
much cleaner and clearer than the collection of nested IFs.

   -- MJF
From: Barry Margolin
Subject: Re: How to pass on only unrecognized keyword params?
Date: 
Message-ID: <barmar-2289D0.13181613032005@comcast.dca.giganews.com>
In article <··········@x-privat.org>,
 M Jared Finder <·····@hpalace.com> wrote:

> Thank you very much; I did not know about the macro REMF.  That's so 
> much cleaner and clearer than the collection of nested IFs.

A simpler way is:

  (apply #'make-hash-table :allow-other-keys t rest)

When you include :ALLOW-OTHER-KEYS in a keyword argument list, it 
automatically disables keyword validity checking.  This is provided 
specifically to address your problem.

-- 
Barry Margolin, ······@alum.mit.edu
Arlington, MA
*** PLEASE post questions in newsgroups, not directly to me ***
From: M Jared Finder
Subject: Re: How to pass on only unrecognized keyword params?
Date: 
Message-ID: <42349815_5@x-privat.org>
Barry Margolin wrote:
> In article <··········@x-privat.org>,
>  M Jared Finder <·····@hpalace.com> wrote:
> 
> 
>>Thank you very much; I did not know about the macro REMF.  That's so 
>>much cleaner and clearer than the collection of nested IFs.
> 
> 
> A simpler way is:
> 
>   (apply #'make-hash-table :allow-other-keys t rest)
> 
> When you include :ALLOW-OTHER-KEYS in a keyword argument list, it 
> automatically disables keyword validity checking.  This is provided 
> specifically to address your problem.

Even better.  And from reading the hyperspec, this can be applied to any 
function that takes keyword parameters!

   -- MJF
From: Kalle Olavi Niemitalo
Subject: Re: How to pass on only unrecognized keyword params?
Date: 
Message-ID: <878y4rkqyt.fsf@Astalo.kon.iki.fi>
Barry Margolin <······@alum.mit.edu> writes:

> When you include :ALLOW-OTHER-KEYS in a keyword argument list, it 
> automatically disables keyword validity checking.  This is provided 
> specifically to address your problem.

I see in section 1.6:

  An implementation can have extensions, provided they do not
  alter the behavior of conforming code and provided they are
  not explicitly prohibited by this standard.

Suppose an implementation has an extension where MAKE-HASH-TABLE
recognizes a :KEY-TYPE argument that specifies the permitted
types of keys in that hash table.  Then, (make-hash-table
:key-type 42 :allow-other-keys t) is likely to fail because
42 is not a type specifier, even though it would succeed in
implementations that do not have extensions.

Does such an extension render an implementation not conforming?
If not, I think using :ALLOW-OTHER-KEYS T with CL functions is
generally a bad idea.

If the extension used EXT:KEY-TYPE rather than KEYWORD:KEY-TYPE,
would that help conformance?
From: Barry Margolin
Subject: Re: How to pass on only unrecognized keyword params?
Date: 
Message-ID: <barmar-AD096B.23340813032005@comcast.dca.giganews.com>
In article <··············@Astalo.kon.iki.fi>,
 Kalle Olavi Niemitalo <···@iki.fi> wrote:

> Barry Margolin <······@alum.mit.edu> writes:
> 
> > When you include :ALLOW-OTHER-KEYS in a keyword argument list, it 
> > automatically disables keyword validity checking.  This is provided 
> > specifically to address your problem.
> 
> I see in section 1.6:
> 
>   An implementation can have extensions, provided they do not
>   alter the behavior of conforming code and provided they are
>   not explicitly prohibited by this standard.
> 
> Suppose an implementation has an extension where MAKE-HASH-TABLE
> recognizes a :KEY-TYPE argument that specifies the permitted
> types of keys in that hash table.  Then, (make-hash-table
> :key-type 42 :allow-other-keys t) is likely to fail because
> 42 is not a type specifier, even though it would succeed in
> implementations that do not have extensions.
> 
> Does such an extension render an implementation not conforming?
> If not, I think using :ALLOW-OTHER-KEYS T with CL functions is
> generally a bad idea.

While this collision is possible, it's unlikely enough that I don't 
think anyone bothers to code against it.

-- 
Barry Margolin, ······@alum.mit.edu
Arlington, MA
*** PLEASE post questions in newsgroups, not directly to me ***
From: Christopher C. Stacy
Subject: Re: How to pass on only unrecognized keyword params?
Date: 
Message-ID: <uu0ng2itk.fsf@news.dtpq.com>
Can you show an example of how you're going to use this facility in a program?
From: Marco Antoniotti
Subject: Re: How to pass on only unrecognized keyword params?
Date: 
Message-ID: <LO%Yd.51$fp1.70204@typhoon.nyu.edu>
(defun make-elt-hash-table (seq &rest keys
                                 &key
                                 key

                                 ;; HASH-TABLE keys
                                 ;; Add defaults as needed.
                                 test
                                 size
                                 rehash-size
                                 rehash-threshold

                                 ;; Impl. dependent keys
                                 &allow-other-keys)
    (remf keys :key)
    (let ((new-ht (apply #'make-hash-table keys))
          (i 0)
         )
       (map nil (lambda (e) (setf (gethash e new-ht) i) (incf i)) seq)
       new-ht))



M Jared Finder wrote:
> I'm writing a small helper function, MAKE-ELT-HASH-TABLE, which creates
> a hash table that maps objects to their indexes, so I don't have to call
> POSITION in a loop.  Ideally, I'd like this function to act just like
> MAKE-HASH-TABLE, but take two extra parameters, a sequence, and a
> keyword parameter for a filter to be applied.
> 
> The only way I can think of to pass on only the unrecognized keyword
> parameters to MAKE-HASH-TABLE is to create a set of huge, nested,
> conditions like this:
> 
>> (defun make-elt-hash-table (seq &key (key #'identity) (test #'eql)
>>                                      (size 0 size-specified?) 
>> (rehash-size 0 rehash-size-specified?)
>>                                      (rehash-threshold 0 
>> rehash-threshold-specified?))
>>   "Create a hash table that maps from (KEY <object-in-seq>) to indexes 
>> that can get passed to ELT."
>>   (flet ((make-hash-table-params ()
>>            (if size-specified?
>>                (if rehash-size-specified?
>>                    (if rehash-threshold-specified?
>>                        (make-hash-table :test test :size size 
>> :rehash-size rehash-size                                         
>> :rehash-threshold rehash-threshold)
>>                        (make-hash-table :test test :size size 
>> :rehash-size rehash-size))
>>                    (if rehash-threshold-specified?
>>                        (make-hash-table :test test :size size 
>> :rehash-threshold rehash-threshold)
>>                        (make-hash-table :test test :size size)))
>>                (if rehash-size-specified?
>>                    (if rehash-threshold-specified?
>>                        (make-hash-table :test test :rehash-size 
>> rehash-size                                         :rehash-threshold 
>> rehash-threshold)
>>                        (make-hash-table :test test :rehash-size 
>> rehash-size))
>>                    (if rehash-threshold-specified?
>>                        (make-hash-table :test test :rehash-threshold 
>> rehash-threshold)
>>                        (make-hash-table :test test))))))
>>     (let ((accum (make-hash-table-params))
>>           (index 0))
>>       (doseq (elem seq)
>>         (setf (gethash (funcall key elem) accum) index)
>>         (incf index))
>>       accum)))
> 
> 
> There must be a better way; I just can't think of it.  How could I do 
> this in a clean way?
> 
>   -- MJF