unofficial mirror of emacs-devel@gnu.org 
 help / color / mirror / code / Atom feed
* [PATCH] Add new function to test whether a key is present in a hash table.
@ 2018-02-15 20:34 Philipp Stephani
  2018-02-15 22:03 ` Clément Pit-Claudel
  2018-02-15 22:59 ` Stefan Monnier
  0 siblings, 2 replies; 13+ messages in thread
From: Philipp Stephani @ 2018-02-15 20:34 UTC (permalink / raw)
  To: emacs-devel; +Cc: Philipp Stephani

Such a function is useful because in Emacs Lisp, 'gethash' cannot
return whether the key is present as in Common Lisp, and using
'gethash' alone to test for presence is nontrivial.

* src/fns.c (Fhash_table_contains_p): New function.
* test/src/fns-tests.el (hash-table-contains-p): New unit test.
* doc/lispref/hash.texi (Hash Access): Document new function.
---
 doc/lispref/hash.texi | 12 ++++++++++++
 etc/NEWS              |  3 +++
 src/fns.c             | 10 ++++++++++
 test/src/fns-tests.el |  8 ++++++++
 4 files changed, 33 insertions(+)

diff --git a/doc/lispref/hash.texi b/doc/lispref/hash.texi
index ddd46a55ed..8848f0462e 100644
--- a/doc/lispref/hash.texi
+++ b/doc/lispref/hash.texi
@@ -195,6 +195,18 @@ Hash Access
 association in @var{table}.
 @end defun
 
+While it might be tempting to use @code{gethash} to check whether a
+key is present in a hash table, keep in mind that you often need to
+distinguish between @var{key} being absent and @var{key} being mapped
+to @var{default}.  To easily distinguish between these two cases,
+there's another function to explicitly check whether a key is present:
+
+@defun hash-table-contains-p key table
+This function looks up @var{key} in @var{table}; it returns @code{t}
+if @var{key} is present, and @code{nil} otherwise.  The associated
+value is ignored.
+@end defun
+
 @defun puthash key value table
 This function enters an association for @var{key} in @var{table}, with
 value @var{value}.  If @var{key} already has an association in
diff --git a/etc/NEWS b/etc/NEWS
index 71569c95ad..5c9363c052 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -282,6 +282,9 @@ depending on the new customizable variable 'read-answer-short'.
 Specifically, it puts the module name into 'load-history', prints
 loading messages if requested, and protects against recursive loads.
 
+** New function 'hash-table-contains-p' to check whether a hash table
+contains a certain key.
+
 \f
 * Changes in Emacs 27.1 on Non-Free Operating Systems
 
diff --git a/src/fns.c b/src/fns.c
index 47457e44c8..6075544c69 100644
--- a/src/fns.c
+++ b/src/fns.c
@@ -4650,6 +4650,15 @@ DEFUN ("clrhash", Fclrhash, Sclrhash, 1, 1, 0,
 }
 
 
+DEFUN ("hash-table-contains-p", Fhash_table_contains_p, Shash_table_contains_p,
+       2, 2, NULL,
+       doc: /* Return t if TABLE contains KEY, nil otherwise.  */)
+  (Lisp_Object key, Lisp_Object table)
+{
+  return hash_lookup (check_hash_table (table), key, NULL) >= 0 ? Qt : Qnil;
+}
+
+
 DEFUN ("gethash", Fgethash, Sgethash, 2, 3, 0,
        doc: /* Look up KEY in TABLE and return its associated value.
 If KEY is not found, return DFLT which defaults to nil.  */)
@@ -5145,6 +5154,7 @@ syms_of_fns (void)
   defsubr (&Shash_table_weakness);
   defsubr (&Shash_table_p);
   defsubr (&Sclrhash);
+  defsubr (&Shash_table_contains_p);
   defsubr (&Sgethash);
   defsubr (&Sputhash);
   defsubr (&Sremhash);
diff --git a/test/src/fns-tests.el b/test/src/fns-tests.el
index f8554636ba..f651ee8985 100644
--- a/test/src/fns-tests.el
+++ b/test/src/fns-tests.el
@@ -575,4 +575,12 @@ dot2
                                :type 'wrong-type-argument)
                  '(wrong-type-argument plistp (:foo 1 . :bar)))))
 
+(ert-deftest hash-table-contains-p ()
+  (let ((h (make-hash-table)))
+    (puthash 1.5 'foo h)
+    (puthash 2.5 nil h)
+    (should (equal (hash-table-contains-p 1.5 h) t))
+    (should (equal (hash-table-contains-p 2.5 h) t))
+    (should (equal (hash-table-contains-p 2 h) nil))))
+
 (provide 'fns-tests)
-- 
2.16.1




^ permalink raw reply related	[flat|nested] 13+ messages in thread

* Re: [PATCH] Add new function to test whether a key is present in a hash table.
  2018-02-15 20:34 Philipp Stephani
@ 2018-02-15 22:03 ` Clément Pit-Claudel
  2019-04-19 17:33   ` Philipp Stephani
  2018-02-15 22:59 ` Stefan Monnier
  1 sibling, 1 reply; 13+ messages in thread
From: Clément Pit-Claudel @ 2018-02-15 22:03 UTC (permalink / raw)
  To: emacs-devel

On 2018-02-15 15:34, Philipp Stephani wrote:
> Such a function is useful because in Emacs Lisp, 'gethash' cannot
> return whether the key is present as in Common Lisp, and using
> 'gethash' alone to test for presence is nontrivial.

Looks useful to me :) Maybe the function should be called …-contains-key-p instead of …-contains-key?

Clément.



^ permalink raw reply	[flat|nested] 13+ messages in thread

* RE: [PATCH] Add new function to test whether a key is present in a hash table.
       [not found] <<20180215203406.64372-1-phst@google.com>
@ 2018-02-15 22:08 ` Drew Adams
  2018-02-15 23:35   ` Eric Abrahamsen
  2018-02-16  6:51   ` John Wiegley
  0 siblings, 2 replies; 13+ messages in thread
From: Drew Adams @ 2018-02-15 22:08 UTC (permalink / raw)
  To: Philipp Stephani, emacs-devel; +Cc: Philipp Stephani

> Such a function is useful because in Emacs Lisp, 'gethash' cannot
> return whether the key is present as in Common Lisp, and using
> 'gethash' alone to test for presence is nontrivial.

Glad to see this.  Everyone was coding their own (in Lisp).

> +While it might be tempting to use @code{gethash} to check whether a
> +key is present in a hash table, keep in mind that you often need to
> +distinguish between @var{key} being absent and @var{key} being mapped
> +to @var{default}.  To easily distinguish between these two cases,
> +there's another function to explicitly check whether a key is present:
> +
> +@defun hash-table-contains-p key table
> +This function looks up @var{key} in @var{table}; it returns @code{t}
> +if @var{key} is present, and @code{nil} otherwise.  The associated
> +value is ignored.

1. I don't think we need to say "The associated value is
ignored".  Nothing suggests that it would be used in any way.

2. The function name should have `key' in it, I think.  Suggestion:
`hash-table-contains-key-p' or even just `hash-table-key-p'.

3. Another possibility could be to return, as the true value,
a cons of the value associated with the key.  IOW, instead
of returning `t' we would return `(THE-VALUE)'.   If this
is not particularly costly it might be a bit more useful.

___

Related: Bug #30458 asks for a function that tests whether
a hash table contains an entry with a given value.  More
precisely, it would return the list of keys with that value,
so nil would mean the value is absent altogether.



^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH] Add new function to test whether a key is present in a hash table.
  2018-02-15 20:34 Philipp Stephani
  2018-02-15 22:03 ` Clément Pit-Claudel
@ 2018-02-15 22:59 ` Stefan Monnier
  2018-02-16  1:03   ` Drew Adams
  1 sibling, 1 reply; 13+ messages in thread
From: Stefan Monnier @ 2018-02-15 22:59 UTC (permalink / raw)
  To: emacs-devel

> Such a function is useful because in Emacs Lisp, 'gethash' cannot
> return whether the key is present as in Common Lisp, and using
> 'gethash' alone to test for presence is nontrivial.

We could also implement it in Elisp:

    (defun hash-table-contains-p (key table)
      (let ((x '(:hash-table-contains-p)))
        (not (eq x (gethash key table x)))))


-- Stefan




^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH] Add new function to test whether a key is present in a hash table.
  2018-02-15 22:08 ` [PATCH] Add new function to test whether a key is present in a hash table Drew Adams
@ 2018-02-15 23:35   ` Eric Abrahamsen
  2018-02-16  6:51   ` John Wiegley
  1 sibling, 0 replies; 13+ messages in thread
From: Eric Abrahamsen @ 2018-02-15 23:35 UTC (permalink / raw)
  To: Drew Adams; +Cc: Philipp Stephani, Philipp Stephani, emacs-devel

Drew Adams <drew.adams@oracle.com> writes:

>> Such a function is useful because in Emacs Lisp, 'gethash' cannot
>> return whether the key is present as in Common Lisp, and using
>> 'gethash' alone to test for presence is nontrivial.
>
> Glad to see this.  Everyone was coding their own (in Lisp).

+1

>> +While it might be tempting to use @code{gethash} to check whether a
>> +key is present in a hash table, keep in mind that you often need to
>> +distinguish between @var{key} being absent and @var{key} being mapped
>> +to @var{default}.  To easily distinguish between these two cases,
>> +there's another function to explicitly check whether a key is present:
>> +
>> +@defun hash-table-contains-p key table
>> +This function looks up @var{key} in @var{table}; it returns @code{t}
>> +if @var{key} is present, and @code{nil} otherwise.  The associated
>> +value is ignored.
>
> 1. I don't think we need to say "The associated value is
> ignored".  Nothing suggests that it would be used in any way.
>
> 2. The function name should have `key' in it, I think.  Suggestion:
> `hash-table-contains-key-p' or even just `hash-table-key-p'.
>
> 3. Another possibility could be to return, as the true value,
> a cons of the value associated with the key.  IOW, instead
> of returning `t' we would return `(THE-VALUE)'.   If this
> is not particularly costly it might be a bit more useful.

I would really like to see a function that didn't require us to:

(and (hash-table-key-p "key" <table>) (gethash "key" <table>))

Ie, an all-in-one function (happy to see it implemented in elisp, a la
Stefan's solution) that gives us both pieces of information at once.
Drew's cons-cell seems like a good middle ground. We could even call it
`hash-table-get', to contrast with `gethash', and still have it accept
(and return) the optional DFLT argument.

Eric



^ permalink raw reply	[flat|nested] 13+ messages in thread

* RE: [PATCH] Add new function to test whether a key is present in a hash table.
  2018-02-15 22:59 ` Stefan Monnier
@ 2018-02-16  1:03   ` Drew Adams
  2018-02-16  1:59     ` Stefan Monnier
  0 siblings, 1 reply; 13+ messages in thread
From: Drew Adams @ 2018-02-16  1:03 UTC (permalink / raw)
  To: Stefan Monnier, emacs-devel

> > Such a function is useful because in Emacs Lisp, 'gethash'
> > cannot return whether the key is present as in Common Lisp,
> > and using 'gethash' alone to test for presence is nontrivial.
> 
> We could also implement it in Elisp:
> (defun hash-table-contains-p (key table)
>   (let ((x '(:hash-table-contains-p)))
>     (not (eq x (gethash key table x)))))

Yes, as I said:
  Everyone was coding their own (in Lisp).

using a unique cons, uninterned symbol, or
some other unique object.

Philipp used an uninterned symbol:
(let ((uniq-symb  '#:void))
  (not (eq uniq-symb (gethash key table uniq-symb))))

I (like you) used a unique cons:
(let ((uniq-cons  (cons 1 1)))
  (not (eq uniq-cons (gethash key table uniq-cons))))

But isn't it better to define this in C?

https://debbugs.gnu.org/cgi/bugreport.cgi?bug=28753



^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH] Add new function to test whether a key is present in a hash table.
  2018-02-16  1:03   ` Drew Adams
@ 2018-02-16  1:59     ` Stefan Monnier
  2018-02-16  2:50       ` Drew Adams
  0 siblings, 1 reply; 13+ messages in thread
From: Stefan Monnier @ 2018-02-16  1:59 UTC (permalink / raw)
  To: emacs-devel

> But isn't it better to define this in C?

To me the answer is usually no, unless it's speed-critical.


        Stefan


PS: I think a more interesting function to provide would be some
gethash-ref which would return some kind of "reference" to the entry, so
we can afterwards update that hash-table entry without (re)computing the
hash (i.e. some kind of equivalent to `intern` after which you can just
do `set` which doesn't involve hashing any more).  But that would imply
a fairly significant amount of design to make it work.




^ permalink raw reply	[flat|nested] 13+ messages in thread

* RE: [PATCH] Add new function to test whether a key is present in a hash table.
  2018-02-16  1:59     ` Stefan Monnier
@ 2018-02-16  2:50       ` Drew Adams
  0 siblings, 0 replies; 13+ messages in thread
From: Drew Adams @ 2018-02-16  2:50 UTC (permalink / raw)
  To: Stefan Monnier, emacs-devel

> > But isn't it better to define this in C?
> 
> To me the answer is usually no, unless it's speed-critical.

To me too, the answer is usually no.  If this is done
in Lisp it is presumably because it might be useful to
someone to modify or advise, or at least serve as food
for thought.  In that case, the code should be as clear
as possible.

> PS: I think a more interesting function to provide would be some
> gethash-ref which would return some kind of "reference" to the entry, so
> we can afterwards update that hash-table entry without (re)computing the
> hash (i.e. some kind of equivalent to `intern` after which you can just
> do `set` which doesn't involve hashing any more).

Definitely more interesting.

> But that would imply
> a fairly significant amount of design to make it work.



^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH] Add new function to test whether a key is present in a hash table.
  2018-02-15 22:08 ` [PATCH] Add new function to test whether a key is present in a hash table Drew Adams
  2018-02-15 23:35   ` Eric Abrahamsen
@ 2018-02-16  6:51   ` John Wiegley
  2018-02-18 14:29     ` Richard Copley
  2018-02-19 17:26     ` Drew Adams
  1 sibling, 2 replies; 13+ messages in thread
From: John Wiegley @ 2018-02-16  6:51 UTC (permalink / raw)
  To: Drew Adams; +Cc: Philipp Stephani, Philipp Stephani, emacs-devel

>>>>> "DA" == Drew Adams <drew.adams@oracle.com> writes:

DA> 2. The function name should have `key' in it, I think. Suggestion:
DA> `hash-table-contains-key-p' or even just `hash-table-key-p'.

It seems a bit strange that the name isn't something like "hashash", since
knowing "puthash" and "gethash" would never help you guess
hash-table-contains-key-p.

-- 
John Wiegley                  GPG fingerprint = 4710 CF98 AF9B 327B B80F
http://newartisans.com                          60E1 46C4 BD1A 7AC1 4BA2



^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH] Add new function to test whether a key is present in a hash table.
  2018-02-16  6:51   ` John Wiegley
@ 2018-02-18 14:29     ` Richard Copley
  2018-02-19  6:02       ` John Wiegley
  2018-02-19 17:26     ` Drew Adams
  1 sibling, 1 reply; 13+ messages in thread
From: Richard Copley @ 2018-02-18 14:29 UTC (permalink / raw)
  To: Drew Adams, Philipp Stephani, Emacs Development, Philipp Stephani

On 16 February 2018 at 06:51, John Wiegley <johnw@gnu.org> wrote:
>>>>>> "DA" == Drew Adams <drew.adams@oracle.com> writes:
>
> DA> 2. The function name should have `key' in it, I think. Suggestion:
> DA> `hash-table-contains-key-p' or even just `hash-table-key-p'.
>
> It seems a bit strange that the name isn't something like "hashash", since
> knowing "puthash" and "gethash" would never help you guess
> hash-table-contains-key-p.

Or "testhash"? And maybe "tryhash" or "try-gethash" for a function that
returns a cons or "slot" of some kind.



^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH] Add new function to test whether a key is present in a hash table.
  2018-02-18 14:29     ` Richard Copley
@ 2018-02-19  6:02       ` John Wiegley
  0 siblings, 0 replies; 13+ messages in thread
From: John Wiegley @ 2018-02-19  6:02 UTC (permalink / raw)
  To: Richard Copley
  Cc: Philipp Stephani, Philipp Stephani, Drew Adams, Emacs Development

>>>>> "RC" == Richard Copley <rcopley@gmail.com> writes:

RC> Or "testhash"? And maybe "tryhash" or "try-gethash" for a function that
RC> returns a cons or "slot" of some kind.

I like tryhash. All of the hash-table functions right now are a bit
unfortunately inconsistent in their naming. I'd rather have hash-table-get,
hash-table-has-key, etc.

-- 
John Wiegley                  GPG fingerprint = 4710 CF98 AF9B 327B B80F
http://newartisans.com                          60E1 46C4 BD1A 7AC1 4BA2



^ permalink raw reply	[flat|nested] 13+ messages in thread

* RE: [PATCH] Add new function to test whether a key is present in a hash table.
  2018-02-16  6:51   ` John Wiegley
  2018-02-18 14:29     ` Richard Copley
@ 2018-02-19 17:26     ` Drew Adams
  1 sibling, 0 replies; 13+ messages in thread
From: Drew Adams @ 2018-02-19 17:26 UTC (permalink / raw)
  To: John Wiegley; +Cc: Philipp Stephani, Philipp Stephani, emacs-devel

> DA> 2. The function name should have `key' in it, I think. Suggestion:
> DA> `hash-table-contains-key-p' or even just `hash-table-key-p'.
> 
> It seems a bit strange that the name isn't something like "hashash",
> since knowing "puthash" and "gethash" would never help you guess
> hash-table-contains-key-p.

Right.  And knowing `hash-table-contains-key-p' wouldn't
help you guess `puthash' or `get-hash'.

But that's the fault of `puthash' and `gethash'. ;-)

It's `puthash' and `gethash' that are not so conventional
for Emacs.  They are borrowed from Common Lisp.  Likewise,
`maphash', `remhash', and `clrhash'.  Those names predate
Common Lisp even.

`puthash', `gethash', `remhash', etc. could be made
aliases of, say, `hash-table-put', `hash-table-get',
`hash-table-remove', etc.  (I'm not saying they should.)

`puthash' seems to be the only Emacs `put*' function
whose name doesn't start with `put-' (apart from `put'
itself).  Same thing for `gethash' (except that there
are also `getenv' and `getf').  The Emacs convention is
to use prefix `put-' or `get-'.

Another (more common?) convention in Emacs is to put the
object type first: `string-match', `bool-vector-union',
`mode-line-next-buffer', `menu-bar-open', `marker-buffer',
`buffer-size', `window-child', `frame-edges', etc.,
especially for functions that extract parts of an object.

Yet another convention is to put the verb first,
especially for "utility" verbs: `map-*', `make-*',
`put-*', `get-*', `copy-*', `delete-*', etc.

Finally, a library prefix is often used: `ad-get-*',
`ange-ftp-get-*', `bookmark-get-*', `comint-get-*',
`dired-get-*', `ediff-get-*', `gnus-get-*' ...


These are the functions I see with "hash" in their names:

0. Those that (I guess) have nothing to do with hash tables:

   ange-ftp-guess-hash-mark-size
   ange-ftp-process-handle-hash
   buffer-hash
   gnutls-hash-digest
   hashcash-insert-payment
   hashcash-insert-payment-async
   hashcash-verify-payment
   rfc2104-hash
   sxhash
   sxhash-eq
   sxhash-eql
   sxhash-equal

   I mention these to point out that names like `puthash'
   don't really, on their own, make clear that they have
   to do with a hash table.

1. Those that are about hash-table hashing but (I guess)
   don't have a hash table as the main object:

   gnus-create-hash-size
   gnutls-hash-mac
   secure-hash
   secure-hash-algorithms

2. Those (non-CL) that have a hash table as the main object:

   ange-ftp-hash-entry-exists-p
   ange-ftp-hash-table-keys
   clrhash
   copy-hash-table
   define-hash-table-test
   define-translation-hash-table
   gethash
   gnus-make-hashtable
   hash-table-count
   hash-table-p
   hash-table-rehash-size
   hash-table-rehash-threshold
   hash-table-size
   hash-table-test
   hash-table-weakness
   make-hash-table
   maphash
   puthash
   remhash
   ucs-normalize-make-hash-table-from-alist

3. Common-Lisp emulation functions for hash tables:

   cl-clrhash
   cl-gethash
   cl-hash-table-count
   cl-hash-table-p
   cl-make-hash-table
   cl-maphash
   cl-not-hash-table 
   cl-puthash
   cl-remhash

   Note that except for those names inherited from older
   Lisps, Common Lisp spells out `hash-table' in the
   function names.

The most common convention, so far, for Emacs hash-table
functions is to start the function name with `hash-table'.
That accords with the convention of putting the object
name first.



^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH] Add new function to test whether a key is present in a hash table.
  2018-02-15 22:03 ` Clément Pit-Claudel
@ 2019-04-19 17:33   ` Philipp Stephani
  0 siblings, 0 replies; 13+ messages in thread
From: Philipp Stephani @ 2019-04-19 17:33 UTC (permalink / raw)
  To: Clément Pit-Claudel; +Cc: Emacs developers

Am Do., 15. Feb. 2018 um 23:05 Uhr schrieb Clément Pit-Claudel
<cpitclaudel@gmail.com>:
>
> On 2018-02-15 15:34, Philipp Stephani wrote:
> > Such a function is useful because in Emacs Lisp, 'gethash' cannot
> > return whether the key is present as in Common Lisp, and using
> > 'gethash' alone to test for presence is nontrivial.
>
> Looks useful to me :) Maybe the function should be called …-contains-key-p instead of …-contains-key?

It's called ...-contains-p, I think it's clear enough that this tests
for key presence and not value presence.
Any objections to pushing this one-liner to master?



^ permalink raw reply	[flat|nested] 13+ messages in thread

end of thread, other threads:[~2019-04-19 17:33 UTC | newest]

Thread overview: 13+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
     [not found] <<20180215203406.64372-1-phst@google.com>
2018-02-15 22:08 ` [PATCH] Add new function to test whether a key is present in a hash table Drew Adams
2018-02-15 23:35   ` Eric Abrahamsen
2018-02-16  6:51   ` John Wiegley
2018-02-18 14:29     ` Richard Copley
2018-02-19  6:02       ` John Wiegley
2018-02-19 17:26     ` Drew Adams
2018-02-15 20:34 Philipp Stephani
2018-02-15 22:03 ` Clément Pit-Claudel
2019-04-19 17:33   ` Philipp Stephani
2018-02-15 22:59 ` Stefan Monnier
2018-02-16  1:03   ` Drew Adams
2018-02-16  1:59     ` Stefan Monnier
2018-02-16  2:50       ` Drew Adams

Code repositories for project(s) associated with this public inbox

	https://git.savannah.gnu.org/cgit/emacs.git

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).