unofficial mirror of guile-devel@gnu.org 
 help / color / mirror / Atom feed
From: David Thompson <dthompson2@worcester.edu>
To: Mark H Weaver <mhw@netris.org>
Cc: guile-devel <guile-devel@gnu.org>
Subject: Re: [PATCH] Add procedures to convert alists into hash tables
Date: Mon, 21 Oct 2013 23:03:41 -0400	[thread overview]
Message-ID: <5265EB0D.2050802@worcester.edu> (raw)
In-Reply-To: <87r4beftk7.fsf@netris.org>

[-- Attachment #1: Type: text/plain, Size: 9207 bytes --]

Thank you for taking the time to give some really useful feedback, Mark 
Weaver. I appreciate it.

On 10/21/2013 01:00 PM, Mark H Weaver wrote:
> Hi David,
>
>>  From 46b7905727ad2efed2dc1d1aca4d4ad00d8f48c5 Mon Sep 17 00:00:00 2001
>> From: David Thompson <dthompson2@worcester.edu>
>> Date: Sat, 19 Oct 2013 22:43:37 -0400
>> Subject: [PATCH] Add procedures to convert alists into hash tables.
>>
>> * libguile/hashtab.c (scm_alist_to_hash_table, scm_alist_to_hashq_table,
>>    scm_alist_to_hashv_table, scm_alist_to_hashx_table): Add the
>>    equivalent of SRFI-69's alist->hash-table procedure for the native
>>    hash table implementation.
>
> In general, commit logs shouldn't describe what the new procedures do,
> nor should they provide rationale.  Those things belong in code
> comments.  Here, it is sufficient to write "New procedures."
>
> You should also mention the new macro SCM_ALIST_TO_HASH_TABLE, as well
> as the new prototypes in hashtab.h.
>
>> * test-suite/tests/hash.test ("alist->hash-table"): Add tests.
>>
>> * doc/ref/api-compound.texi (alist->hash-table): Add docs.
>
> For .texi files, the part in parentheses here should be the node name
> where the changes were made.  To find it, I usually search backwards for
> "@node".  In this case, the node name is "Hash Table Reference".

Good to know. Still trying to get used to this commit format.

>
>> ---
>>   doc/ref/api-compound.texi  | 18 ++++++++++++++
>>   libguile/hashtab.c         | 61 ++++++++++++++++++++++++++++++++++++++++++++++
>>   libguile/hashtab.h         |  6 +++++
>>   test-suite/tests/hash.test | 27 ++++++++++++++++++++
>>   4 files changed, 112 insertions(+)
>>
>> diff --git a/doc/ref/api-compound.texi b/doc/ref/api-compound.texi
>> index 94e0145..06115be 100644
>> --- a/doc/ref/api-compound.texi
>> +++ b/doc/ref/api-compound.texi
>> @@ -3829,6 +3829,24 @@ then it can use @var{size} to avoid rehashing when initial entries are
>>   added.
>>   @end deffn
>>
>> +@deffn {Scheme Procedure} alist->hash-table alist [size]
>> +@deffnx {Scheme Procedure} alist->hashq-table alist [size]
>> +@deffnx {Scheme Procedure} alist->hashv-table alist [size]
>> +@deffnx {Scheme Procedure} alist->hashx-table hash assoc alist [size]
>> +@deffnx {C Function} scm_alist_to_hash_table (alist, size)
>> +@deffnx {C Function} scm_alist_to_hashq_table (alist, size)
>> +@deffnx {C Function} scm_alist_to_hashv_table (alist, size)
>> +@deffnx {C Function} scm_alist_to_hashx_table (hash, assoc, alist, size)
>> +Convert @var{alist} into a hash table with minimum number of buckets
>> +@var{size}. When keys are repeated in @var{alist}, the leftmost
>> +association takes precedence.
>
> A few thoughts:
>
> * Are you sure that the leftmost association takes precedence?  From
>    looking at the code, I'd expect the _rightmost_ association to take
>    precedence.  In either case, since it's mentioned explicitly in the
>    docs, it would be good if the test suite checked this.

You are right. I have changed the code such that the leftmost 
association actually takes precedence and the tests check for this.
>
> * For 'alist->hashx-table' (and 'scm_alist_to_hashx_table') the 'hash'
>    and 'assoc' arguments should probably be mentioned, and it would be
>    good to include an example of its use.
>
> * I'm not sure the 'size' argument is worth keeping here.  It seems to
>    me that it will rarely be used.  It might be better to instead compute
>    the size automatically from the alist.  What do you think?
>
>    If you did this, you should use the internal 'scm_ilength' function,
>    which is robust against improper lists and even circular lists, in
>    which case it returns -1.  (In fact, SCM_VALIDATE_LIST works by simply
>    calling scm_ilength and checking that the result is non-negative.)

'size' argument is gone in favor of using 'scm_ilength'.

>
>> +@example
>> +(alist->hash-table '((foo . 1) (bar . 2)))
>> +@end example
>> +
>> +@end deffn
>> +
>>   @deffn {Scheme Procedure} hash-table? obj
>>   @deffnx {C Function} scm_hash_table_p (obj)
>>   Return @code{#t} if @var{obj} is a abstract hash table object.
>> diff --git a/libguile/hashtab.c b/libguile/hashtab.c
>> index 88cb199..c215269 100644
>> --- a/libguile/hashtab.c
>> +++ b/libguile/hashtab.c
>> @@ -423,6 +423,67 @@ SCM_DEFINE (scm_make_hash_table, "make-hash-table", 0, 1, 0,
>>   }
>>   #undef FUNC_NAME
>>
>> +#define SCM_ALIST_TO_HASH_TABLE(alist, n, hash_set_fn) \
>> +  SCM hash_table; \
>> +  SCM_VALIDATE_LIST (1, alist); \
>> +  hash_table = scm_make_hash_table (n); \
>> +  while (!scm_is_null (alist)) { \
>> +    SCM pair = SCM_CAR (alist); \
>> +    hash_set_fn (hash_table, scm_car (pair), scm_cdr (pair));   \
>> +    alist = SCM_CDR (alist); \
>> +  } \
>> +  return hash_table;
>
> This is a nitpick, but according to our usual style, the backslashes
> should be lined up in a single column.

Much prettier.

>
>> +
>> +SCM_DEFINE (scm_alist_to_hash_table, "alist->hash-table", 1, 1, 0,
>> +	    (SCM alist, SCM n),
>
> More nitpicking: there's a tab in the line above, which messes up the
> indentation.

When kill/yank goes wrong. Fixed.

>
>> +            "Convert @var{alist} into a hash table with minimum number of "
>> +            "buckets @var{n}.")
>> +#define FUNC_NAME s_scm_alist_to_hash_table
>> +{
>> +  SCM_ALIST_TO_HASH_TABLE (alist, n, scm_hash_set_x);
>> +}
>> +#undef FUNC_NAME
>> +
>> +SCM_DEFINE (scm_alist_to_hashq_table, "alist->hashq-table", 1, 1, 0,
>> +	    (SCM alist, SCM n),
>
> Ditto ^^
>
>> +            "Convert @var{alist} into a hash table with minimum number of "
>> +            "buckets @var{n}.")
>> +#define FUNC_NAME s_scm_alist_to_hashq_table
>> +{
>> +  SCM_ALIST_TO_HASH_TABLE (alist, n, scm_hashq_set_x);
>> +}
>> +#undef FUNC_NAME
>> +
>> +SCM_DEFINE (scm_alist_to_hashv_table, "alist->hashv-table", 1, 1, 0,
>> +	    (SCM alist, SCM n),
>
> Ditto ^^
>
>> +            "Convert @var{alist} into a hash table with minimum number of "
>> +            "buckets @var{n}.")
>> +#define FUNC_NAME s_scm_alist_to_hashv_table
>> +{
>> +  SCM_ALIST_TO_HASH_TABLE (alist, n, scm_hashv_set_x);
>> +}
>> +#undef FUNC_NAME
>> +
>> +SCM_DEFINE (scm_alist_to_hashx_table, "alist->hashx-table", 3, 1, 0,
>> +	    (SCM hash, SCM assoc, SCM alist, SCM n),
>
> Ditto ^^
>
>> +            "Convert @var{alist} into a hash table with minimum number of "
>> +            "buckets @var{n}.")
>
> Please mention the 'hash' and 'assoc' arguments in the docstring.

Mentioned.

>
>> +#define FUNC_NAME s_scm_alist_to_hashx_table
>> +{
>> +  SCM hash_table;
>> +  SCM_VALIDATE_LIST (3, alist);
>> +  hash_table = scm_make_hash_table (n);
>> +
>> +  while (!scm_is_null (alist)) {
>> +    SCM pair = SCM_CAR (alist);
>> +    scm_hashx_set_x (hash, assoc, hash_table, scm_car (pair), scm_cdr (pair));
>> +    alist = SCM_CDR (alist);
>> +  }
>> +
>> +  return hash_table;
>> +}
>> +#undef FUNC_NAME
>> +
>>   /* The before-gc C hook only runs if GC_set_start_callback is available,
>>      so if not, fall back on a finalizer-based implementation.  */
>>   static int
>> diff --git a/libguile/hashtab.h b/libguile/hashtab.h
>> index dcebcb8..da4f28c 100644
>> --- a/libguile/hashtab.h
>> +++ b/libguile/hashtab.h
>> @@ -101,6 +101,12 @@ SCM_API SCM scm_make_weak_key_hash_table (SCM k);
>>   SCM_API SCM scm_make_weak_value_hash_table (SCM k);
>>   SCM_API SCM scm_make_doubly_weak_hash_table (SCM k);
>>
>> +SCM_API SCM scm_alist_to_hash_table (SCM alist, SCM n);
>> +SCM_API SCM scm_alist_to_hashq_table (SCM alist, SCM n);
>> +SCM_API SCM scm_alist_to_hashv_table (SCM alist, SCM n);
>> +SCM_API SCM scm_alist_to_hashx_table (SCM hash, SCM assoc,
>> +                                      SCM alist, SCM n);
>> +
>>   SCM_API SCM scm_hash_table_p (SCM h);
>>   SCM_API SCM scm_weak_key_hash_table_p (SCM h);
>>   SCM_API SCM scm_weak_value_hash_table_p (SCM h);
>> diff --git a/test-suite/tests/hash.test b/test-suite/tests/hash.test
>> index 3bd4004..b09c0b8 100644
>> --- a/test-suite/tests/hash.test
>> +++ b/test-suite/tests/hash.test
>> @@ -81,6 +81,33 @@
>>                                 (write (make-hash-table 100)))))))
>>
>>   ;;;
>> +;;; alist->hash-table
>> +;;;
>> +
>> +(with-test-prefix
>> +  "alist->hash-table"
>> +
>> +  ;; equal? hash table
>> +  (pass-if (let ((table (alist->hash-table '(("foo" . 1) ("bar" . 2)))))
>> +             (and (= (hash-ref table "foo") 1)
>> +                  (= (hash-ref table "bar") 2))))
>> +
>> +  ;; eq? hash table
>> +  (pass-if (let ((table (alist->hashq-table '((foo . 1) (bar . 2)))))
>> +             (and (= (hashq-ref table 'foo) 1)
>> +                  (= (hashq-ref table 'bar) 2))))
>> +
>> +  ;; eqv? hash table
>> +  (pass-if (let ((table (alist->hashv-table '((1 . 1) (2 . 2)))))
>> +             (and (= (hashv-ref table 1) 1)
>> +                  (= (hashv-ref table 2) 2))))
>> +
>> +  ;; custom hash table
>> +  (pass-if (let ((table (alist->hashx-table hash assoc '((foo . 1) (bar . 2)))))
>> +             (and (= (hashx-ref hash assoc table 'foo) 1)
>> +                  (= (hashx-ref hash assoc table 'bar) 2)))))
>> +
>> +;;;
>>   ;;; usual set and reference
>>   ;;;

Updated patch attached.

- Dave

[-- Attachment #2: 0001-Add-procedures-to-convert-alists-into-hash-tables.patch --]
[-- Type: text/x-patch, Size: 7587 bytes --]

From f971e341d6ead7ee0c082030672b25fee640be2d Mon Sep 17 00:00:00 2001
From: David Thompson <dthompson2@worcester.edu>
Date: Sat, 19 Oct 2013 22:43:37 -0400
Subject: [PATCH] Add procedures to convert alists into hash tables.

* libguile/hashtab.h (scm_alist_to_hash_table, scm_alist_to_hashq_table,
  scm_alist_to_hashv_table, scm_alist_to_hashx_table): New prototypes.

* libguile/hashtab.c (scm_alist_to_hash_table, scm_alist_to_hashq_table,
  scm_alist_to_hashv_table, scm_alist_to_hashx_table): New procedures.
  (SCM_ALIST_TO_HASH_TABLE): New macro.

* test-suite/tests/hash.test ("alist->hash-table"): Add tests.

* doc/ref/api-compound.texi (Hash Table Reference): Add docs.
---
 doc/ref/api-compound.texi  | 24 +++++++++++++++++
 libguile/hashtab.c         | 67 ++++++++++++++++++++++++++++++++++++++++++++++
 libguile/hashtab.h         |  5 ++++
 test-suite/tests/hash.test | 35 ++++++++++++++++++++++++
 4 files changed, 131 insertions(+)

diff --git a/doc/ref/api-compound.texi b/doc/ref/api-compound.texi
index 94e0145..e13c9c4 100644
--- a/doc/ref/api-compound.texi
+++ b/doc/ref/api-compound.texi
@@ -3829,6 +3829,30 @@ then it can use @var{size} to avoid rehashing when initial entries are
 added.
 @end deffn
 
+@deffn {Scheme Procedure} alist->hash-table alist
+@deffnx {Scheme Procedure} alist->hashq-table alist
+@deffnx {Scheme Procedure} alist->hashv-table alist
+@deffnx {Scheme Procedure} alist->hashx-table hash assoc alist
+@deffnx {C Function} scm_alist_to_hash_table (alist)
+@deffnx {C Function} scm_alist_to_hashq_table (alist)
+@deffnx {C Function} scm_alist_to_hashv_table (alist)
+@deffnx {C Function} scm_alist_to_hashx_table (hash, assoc, alist)
+Convert @var{alist} into a hash table. When keys are repeated in
+@var{alist}, the leftmost association takes precedence.
+
+@example
+(alist->hash-table '((foo . 1) (bar . 2)))
+@end example
+
+When converting to an extended hash table, custom @var{hash} and
+@var{assoc} procedures must be provided.
+
+@example
+(alist->hash-table hash assoc '((foo . 1) (bar . 2)))
+@end example
+
+@end deffn
+
 @deffn {Scheme Procedure} hash-table? obj
 @deffnx {C Function} scm_hash_table_p (obj)
 Return @code{#t} if @var{obj} is a abstract hash table object.
diff --git a/libguile/hashtab.c b/libguile/hashtab.c
index 88cb199..8bf5230 100644
--- a/libguile/hashtab.c
+++ b/libguile/hashtab.c
@@ -423,6 +423,73 @@ SCM_DEFINE (scm_make_hash_table, "make-hash-table", 0, 1, 0,
 }
 #undef FUNC_NAME
 
+#define SCM_ALIST_TO_HASH_TABLE(alist, hash_set_fn, hash_get_handle_fn) \
+  SCM hash_table;                                                       \
+  SCM_VALIDATE_LIST (1, alist);                                         \
+  hash_table = make_hash_table (0, scm_ilength (alist), FUNC_NAME);     \
+  while (!scm_is_null (alist)) {                                        \
+    SCM pair = SCM_CAR (alist);                                         \
+    SCM key = scm_car (pair);                                           \
+    SCM value = scm_cdr (pair);                                         \
+    if (scm_is_false (hash_get_handle_fn (hash_table, key))) {          \
+      hash_set_fn (hash_table, key, value);                             \
+    }                                                                   \
+    alist = SCM_CDR (alist);                                            \
+  }                                                                     \
+  return hash_table;
+
+SCM_DEFINE (scm_alist_to_hash_table, "alist->hash-table", 1, 0, 0,
+            (SCM alist),
+            "Convert @var{alist} into a hash table.")
+#define FUNC_NAME s_scm_alist_to_hash_table
+{
+  SCM_ALIST_TO_HASH_TABLE (alist, scm_hash_set_x, scm_hash_get_handle);
+}
+#undef FUNC_NAME
+
+SCM_DEFINE (scm_alist_to_hashq_table, "alist->hashq-table", 1, 0, 0,
+            (SCM alist),
+            "Convert @var{alist} into a hash table.")
+#define FUNC_NAME s_scm_alist_to_hashq_table
+{
+  SCM_ALIST_TO_HASH_TABLE (alist, scm_hashq_set_x, scm_hashq_get_handle);
+}
+#undef FUNC_NAME
+
+SCM_DEFINE (scm_alist_to_hashv_table, "alist->hashv-table", 1, 0, 0,
+            (SCM alist),
+            "Convert @var{alist} into a hash table.")
+#define FUNC_NAME s_scm_alist_to_hashv_table
+{
+  SCM_ALIST_TO_HASH_TABLE (alist, scm_hashv_set_x, scm_hashv_get_handle);
+}
+#undef FUNC_NAME
+
+SCM_DEFINE (scm_alist_to_hashx_table, "alist->hashx-table", 3, 0, 0,
+            (SCM hash, SCM assoc, SCM alist),
+            "Convert @var{alist} into a hash table with custom @var{hash} and"
+            "@var{assoc} procedures.")
+#define FUNC_NAME s_scm_alist_to_hashx_table
+{
+  SCM hash_table;
+  SCM_VALIDATE_LIST (3, alist);
+  hash_table = make_hash_table (0, scm_ilength (alist), FUNC_NAME);
+
+  while (!scm_is_null (alist)) {
+    SCM pair = SCM_CAR (alist);
+    SCM key = scm_car (pair);
+    SCM value = scm_cdr (pair);
+
+    if (scm_is_false (scm_hashx_get_handle (hash, assoc, hash_table, key))) {
+      scm_hashx_set_x (hash, assoc, hash_table, key, value);
+    }
+    alist = SCM_CDR (alist);
+  }
+
+  return hash_table;
+}
+#undef FUNC_NAME
+
 /* The before-gc C hook only runs if GC_set_start_callback is available,
    so if not, fall back on a finalizer-based implementation.  */
 static int
diff --git a/libguile/hashtab.h b/libguile/hashtab.h
index dcebcb8..270efe9 100644
--- a/libguile/hashtab.h
+++ b/libguile/hashtab.h
@@ -101,6 +101,11 @@ SCM_API SCM scm_make_weak_key_hash_table (SCM k);
 SCM_API SCM scm_make_weak_value_hash_table (SCM k);
 SCM_API SCM scm_make_doubly_weak_hash_table (SCM k);
 
+SCM_API SCM scm_alist_to_hash_table (SCM alist);
+SCM_API SCM scm_alist_to_hashq_table (SCM alist);
+SCM_API SCM scm_alist_to_hashv_table (SCM alist);
+SCM_API SCM scm_alist_to_hashx_table (SCM hash, SCM assoc, SCM alist);
+
 SCM_API SCM scm_hash_table_p (SCM h);
 SCM_API SCM scm_weak_key_hash_table_p (SCM h);
 SCM_API SCM scm_weak_value_hash_table_p (SCM h);
diff --git a/test-suite/tests/hash.test b/test-suite/tests/hash.test
index 3bd4004..820e522 100644
--- a/test-suite/tests/hash.test
+++ b/test-suite/tests/hash.test
@@ -81,6 +81,41 @@
                               (write (make-hash-table 100)))))))
 
 ;;;
+;;; alist->hash-table
+;;;
+
+(with-test-prefix
+  "alist->hash-table"
+
+  ;; equal? hash table
+  (pass-if (let ((table (alist->hash-table '(("foo" . 1)
+                                             ("bar" . 2)
+                                             ("foo" . 3)))))
+             (and (= (hash-ref table "foo") 1)
+                  (= (hash-ref table "bar") 2))))
+
+  ;; eq? hash table
+  (pass-if (let ((table (alist->hashq-table '((foo . 1)
+                                              (bar . 2)
+                                              (foo . 3)))))
+             (and (= (hashq-ref table 'foo) 1)
+                  (= (hashq-ref table 'bar) 2))))
+
+  ;; eqv? hash table
+  (pass-if (let ((table (alist->hashv-table '((1 . 1)
+                                              (2 . 2)
+                                              (1 . 3)))))
+             (and (= (hashv-ref table 1) 1)
+                  (= (hashv-ref table 2) 2))))
+
+  ;; custom hash table
+  (pass-if (let ((table (alist->hashx-table hash assoc '((foo . 1)
+                                                         (bar . 2)
+                                                         (foo . 3)))))
+             (and (= (hashx-ref hash assoc table 'foo) 1)
+                  (= (hashx-ref hash assoc table 'bar) 2)))))
+
+;;;
 ;;; usual set and reference
 ;;;
 
-- 
1.8.4.rc3


  reply	other threads:[~2013-10-22  3:03 UTC|newest]

Thread overview: 23+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2013-10-20 14:14 [PATCH] Add procedures to convert alists into hash tables David Thompson
2013-10-20 14:28 ` David Thompson
2013-10-21 17:00   ` Mark H Weaver
2013-10-22  3:03     ` David Thompson [this message]
2013-10-28 12:17       ` Chris K. Jester-Young
2013-10-28 23:51         ` David Thompson
2013-10-26 11:41     ` David Thompson
2013-10-29 12:38 ` Ludovic Courtès
2013-10-30 20:19   ` Thompson, David
2013-11-03 12:43     ` Thien-Thi Nguyen
2013-11-05 19:57     ` Mark H Weaver
2013-11-06 12:54       ` Ludovic Courtès
2013-11-06 13:14         ` Thompson, David
2013-11-18  1:50         ` David Thompson
2013-11-18  2:42           ` David Thompson
2013-11-18  3:22             ` Mark H Weaver
2013-11-18 20:32             ` Ludovic Courtès
2013-11-19  2:00               ` David Thompson
2013-11-19  4:06                 ` Mark H Weaver
2013-11-19 13:14                   ` Thompson, David
2014-03-24 21:06               ` Andy Wingo
2014-03-24 22:15                 ` Ludovic Courtès
2014-03-24 22:26                   ` Andy Wingo

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

  List information: https://www.gnu.org/software/guile/

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=5265EB0D.2050802@worcester.edu \
    --to=dthompson2@worcester.edu \
    --cc=guile-devel@gnu.org \
    --cc=mhw@netris.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
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).