So this seems to crash inside scm_hash_fn_set_x:
in libguile/hashtab.c:510:
SCM
scm_hash_fn_set_x (SCM table, SCM obj, SCM val, unsigned long (*hash_fn)(),
SCM (*assoc_fn)(), void * closure)
{
SCM it;
it = scm_hash_fn_create_handle_x (table, obj, SCM_BOOL_F, hash_fn, assoc_fn,
closure);
SCM_SETCDR (it, val);
return val;
}
as indicated by:
[trinidad installing-guile/guile-1.8.3]$ gdb libguile/.libs/guile
GNU gdb 6.4.90-debian
Copyright (C) 2006 Free Software Foundation, Inc. [...]
This GDB was configured as "i486-linux-gnu"...Using host libthread_db
library "/lib/tls/libthread_db.so.1".
(gdb) run -l ht.scm
[which contains:
(hashx-set! (lambda (k s) 1) (lambda (a b) #t) (make-hash-table) 'foo 'bar)
]
Starting program:
/afs/csail.mit.edu/u/g/gremio/installing-guile/guile-1.8.3/libguile/.libs/guile -l ht.scm
Failed to read a valid object file image from memory.
[Thread debugging using libthread_db enabled]
[New Thread -1211361600 (LWP 28295)]
Program received signal SIGSEGV, Segmentation fault.
[Switching to Thread -1211361600 (LWP 28295)]
0xb7f008da in scm_hash_fn_set_x () from /usr/lib/libguile.so.17
(gdb) bt
#0 0xb7f008da in scm_hash_fn_set_x () from /usr/lib/libguile.so.17
#1 0xb7f00942 in scm_hashx_set_x () from /usr/lib/libguile.so.17
#2 0xb7efdd5d in scm_gsubr_apply () from /usr/lib/libguile.so.17
This means to me that
(define table (make-hash-table))
(hashx-create-handle! (lambda (k s) 1) (lambda (a b) #t) table 'biz 'buzz)
should not crash, and indeed, it returns #t.
It doesn't help the problem that the association fails, and I wonder if
that's because the hash_fn is supposed to be unsigned long (*hash_fn)().
How does one create one of these in user space? (lambda (k s) 1) is
something more like SCM (*hash_fn)() ?
So if I pass in something that returns scheme objects instead of unsigned
longs, and they get interpreted as unsigned longs, then no wonder I don't
find the key next time. I might be deeply misunderstanding things though.
As another question, when I hash things using hashx-set! and later the
table needs to be resized, is it the closure that's supposed to remember
what function I hashed that key with? I just added a test for this
(enclosed), and despite no segfaults, the test fails -- the hash seems to
forget about some of its items after resizing.
As a design decision, most hash implementations I've seen, if they allow
your own hash function at all, they ask you to specify it at hash creation
time, and they usually do the modulo themselves (so the hash function is
one-argument). The spec for guile seems to be somewhat more flexible, but
at the expense of a fair bit more overhead required to keep things straight
during resize. That is, in a sane implementation, you'd have to remember
the hash function used to put each key in, so you'd have some hope of
getting it back out again. Why did guile choose to spec this way?
Thanks in advance for any input,
Grem
Enclosed is a patch for test-suite/tests/hash.test that contains, among other
things, the segfaulting behavior.
Best,
Grem
I managed to compile 1.8.3 on debian and it shows much the same error,
substituting a Segmentation Fault for bus error in the last case.
Guile 1.3.4 simply returns #f in both cases.
I'm inclined to start poking at guile's source code for the first time.
Could someone give me helpful pointers in the right general direction?
Thanks,
Grem
On Thu, 10 Jan 2008, Gregory Marton wrote:
I may be misunderstanding something, but I thought this should yield 'bar:
guile> (let ((ht (make-hash-table)))
(hashx-set! (lambda (k s) 1) equal? ht 'foo 'bar)
(hashx-ref (lambda (k s) 1) equal? ht 'foo))
#f
guile> (version)
"1.8.1"
much as this does:
guile> (let ((ht (make-hash-table)))
(hash-set! ht 'foo 'bar)
(hash-ref ht 'foo))
bar
I thought perhaps the problem was with the equality test somehow, but then
even worse:
guile> (let ((ht (make-hash-table)))
(hashx-set! (lambda (k s) 1) (lambda (a b) #t) ht 'foo 'bar)
(hashx-ref (lambda (k s) 1) (lambda (a b) #t) ht 'foo))
Bus error
Thanks much,
Grem
p.s. Ludovic, I haven't forgotten about the module docs -- it's just
taking me a while to learn what I need. Sorry.
--
------ __@ Gregory A. Marton http://csail.mit.edu/~gremio/
--- _`\<,_ .
-- (*)/ (*) The prime number few.
~~~~~~~~~~~~~~~~-~~~~~~~~_~~~_~~~~~v~~~~^^^^~~~~~--~~~~~~~~~~~~~~~++~~~~~~~
--- test-suite/tests/hash.test.1.8.3 2008-01-12 04:52:45.000000000 -0500
+++ test-suite/tests/hash.test 2008-01-12 06:27:05.000000000 -0500
@@ -63,6 +63,205 @@
(pass-if (= 0 (hashq noop 1))))
;;;
+;;; make-hash-table
+;;;
+
+(with-test-prefix
+ "make-hash-table, hash-table?"
+ (pass-if-exception "make-hash-table -1" exception:out-of-range
+ (make-hash-table -1))
+ (pass-if (hash-table? (make-hash-table 0))) ;; default
+ (pass-if (not (hash-table? 'not-a-hash-table)))
+ (pass-if (equal? "#<hash-table 0/113>"
+ (with-output-to-string
+ (lambda () (write (make-hash-table 100)))))))
+
+;;;
+;;; usual set and reference
+;;;
+
+(with-test-prefix
+ "hash-set and hash-ref"
+
+ ;; auto-resizing
+ (pass-if (let ((table (make-hash-table 1))) ;;actually makes size 31
+ (hash-set! table 'one 1)
+ (hash-set! table 'two #t)
+ (hash-set! table 'three #t)
+ (hash-set! table 'four #t)
+ (hash-set! table 'five #t)
+ (hash-set! table 'six #t)
+ (hash-set! table 'seven #t)
+ (hash-set! table 'eight #t)
+ (hash-set! table 'nine 9)
+ (hash-set! table 'ten #t)
+ (hash-set! table 'eleven #t)
+ (hash-set! table 'twelve #t)
+ (hash-set! table 'thirteen #t)
+ (hash-set! table 'fourteen #t)
+ (hash-set! table 'fifteen #t)
+ (hash-set! table 'sixteen #t)
+ (hash-set! table 'seventeen #t)
+ (hash-set! table 18 #t)
+ (hash-set! table 19 #t)
+ (hash-set! table 20 #t)
+ (hash-set! table 21 #t)
+ (hash-set! table 22 #t)
+ (hash-set! table 23 #t)
+ (hash-set! table 24 #t)
+ (hash-set! table 25 #t)
+ (hash-set! table 26 #t)
+ (hash-set! table 27 #t)
+ (hash-set! table 28 #t)
+ (hash-set! table 29 #t)
+ (hash-set! table 30 'thirty)
+ (hash-set! table 31 #t)
+ (hash-set! table 32 #t)
+ (hash-set! table 33 'thirty-three)
+ (hash-set! table 34 #t)
+ (hash-set! table 35 #t)
+ (hash-set! table 'foo 'bar)
+ (and (equal? 1 (hash-ref table 'one))
+ (equal? 9 (hash-ref table 'nine))
+ (equal? 'thirty (hash-ref table 30))
+ (equal? 'thirty-three (hash-ref table 33))
+ (equal? 'bar (hash-ref table 'foo))
+ (equal? "#<hash-table 36/61>"
+ (with-output-to-string (lambda () (write table)))))))
+
+ ;; 1 and 1 are equal? and eqv? and eq?
+ (pass-if (equal? 'foo
+ (let ((table (make-hash-table)))
+ (hash-set! table 1 'foo)
+ (hash-ref table 1))))
+ (pass-if (equal? 'foo
+ (let ((table (make-hash-table)))
+ (hashv-set! table 1 'foo)
+ (hashv-ref table 1))))
+ (pass-if (equal? 'foo
+ (let ((table (make-hash-table)))
+ (hashq-set! table 1 'foo)
+ (hashq-ref table 1))))
+
+ ;; 1/2 and 2/4 are equal? and eqv? but not eq?
+ (pass-if (equal? 'foo
+ (let ((table (make-hash-table)))
+ (hash-set! table 1/2 'foo)
+ (hash-ref table 2/4))))
+ (pass-if (equal? 'foo
+ (let ((table (make-hash-table)))
+ (hashv-set! table 1/2 'foo)
+ (hashv-ref table 2/4))))
+ (pass-if (equal? #f
+ (let ((table (make-hash-table)))
+ (hashq-set! table 1/2 'foo)
+ (hashq-ref table 2/4))))
+
+ ;; (list 1 2) is equal? but not eqv? or eq? to another (list 1 2)
+ (pass-if (equal? 'foo
+ (let ((table (make-hash-table)))
+ (hash-set! table (list 1 2) 'foo)
+ (hash-ref table (list 1 2)))))
+ (pass-if (equal? #f
+ (let ((table (make-hash-table)))
+ (hashv-set! table (list 1 2) 'foo)
+ (hashv-ref table (list 1 2)))))
+ (pass-if (equal? #f
+ (let ((table (make-hash-table)))
+ (hashq-set! table (list 1 2) 'foo)
+ (hashq-ref table (list 1 2)))))
+
+ ;; ref default argument
+ (pass-if (equal? 'bar
+ (let ((table (make-hash-table)))
+ (hash-ref table 'foo 'bar))))
+ (pass-if (equal? 'bar
+ (let ((table (make-hash-table)))
+ (hashv-ref table 'foo 'bar))))
+ (pass-if (equal? 'bar
+ (let ((table (make-hash-table)))
+ (hashq-ref table 'foo 'bar))))
+ (pass-if (equal? 'bar
+ (let ((table (make-hash-table)))
+ (hashx-ref hash equal? table 'foo 'bar))))
+
+ ;; wrong type argument
+ (pass-if-exception "(hash-ref 'not-a-table 'key)" exception:wrong-type-arg
+ (hash-ref 'not-a-table 'key))
+ )
+
+;;;
+;;; hashx
+;;;
+
+(with-test-prefix
+ "auto-resizing hashx"
+ ;; auto-resizing
+ (pass-if (let ((table (make-hash-table 1))) ;;actually makes size 31
+ (hashx-set! hash assoc table 1/2 'equal)
+ (hashx-set! hashv assv table 1/3 'eqv)
+ (hashx-set! hashq assq table 4 'eq)
+ (hashx-set! hash assoc table 1/5 'equal)
+ (hashx-set! hashv assv table 1/6 'eqv)
+ (hashx-set! hashq assq table 7 'eq)
+ (hashx-set! hash assoc table 1/8 'equal)
+ (hashx-set! hashv assv table 1/9 'eqv)
+ (hashx-set! hashq assq table 10 'eq)
+ (hashx-set! hash assoc table 1/11 'equal)
+ (hashx-set! hashv assv table 1/12 'eqv)
+ (hashx-set! hashq assq table 13 'eq)
+ (hashx-set! hash assoc table 1/14 'equal)
+ (hashx-set! hashv assv table 1/15 'eqv)
+ (hashx-set! hashq assq table 16 'eq)
+ (hashx-set! hash assoc table 1/17 'equal)
+ (hashx-set! hashv assv table 1/18 'eqv)
+ (hashx-set! hashq assq table 19 'eq)
+ (hashx-set! hash assoc table 1/20 'equal)
+ (hashx-set! hashv assv table 1/21 'eqv)
+ (hashx-set! hashq assq table 22 'eq)
+ (hashx-set! hash assoc table 1/23 'equal)
+ (hashx-set! hashv assv table 1/24 'eqv)
+ (hashx-set! hashq assq table 25 'eq)
+ (hashx-set! hash assoc table 1/26 'equal)
+ (hashx-set! hashv assv table 1/27 'eqv)
+ (hashx-set! hashq assq table 28 'eq)
+ (hashx-set! hash assoc table 1/29 'equal)
+ (hashx-set! hashv assv table 1/30 'eqv)
+ (hashx-set! hashq assq table 31 'eq)
+ (hashx-set! hash assoc table 1/32 'equal)
+ (hashx-set! hashv assv table 1/33 'eqv)
+ (hashx-set! hashq assq table 34 'eq)
+ (and (equal? 'equal (hash-ref table 2/4))
+ (equal? 'eqv (hashv-ref table 2/6))
+ (equal? 'eq (hashq-ref table 4))
+ (equal? 'equal (hashx-ref hash equal? table 2/64))
+ (equal? 'eqv (hashx-ref hashv eqv? table 2/66))
+ (equal? 'eq (hashx-ref hashq eq? table 34))
+ (equal? "#<hash-table 34/61>"
+ (with-output-to-string (lambda () (write table))))))))
+
+(with-test-prefix
+ "hashx"
+ (pass-if (let ((table (make-hash-table)))
+ (hashx-set! (lambda (k v) 1) (lambda (a b) #t) table 'foo 'bar)
+ (equal?
+ 'bar (hashx-ref (lambda (k v) 1) (lambda (a b) #t) table 'baz))))
+ (pass-if (let ((table (make-hash-table)))
+ (hashx-set! (lambda (k v) 1) equal? table 'foo 'bar)
+ (equal?
+ 'bar (hashx-ref (lambda (k v) 1) equal? table 'baz))))
+ (pass-if (let ((table (make-hash-table 31)))
+ (hashx-set! (lambda (k v) 1) equal? table 'foo 'bar)
+ (equal? #f
+ (hashx-ref (lambda (k v) 2) equal? table 'foo))))
+ (pass-if (let ((table (make-hash-table)))
+ (hashx-set! hash equal? table 'foo 'bar)
+ (equal? #f
+ (hashx-ref hash (lambda (a b) #f) table 'foo)))))
+
+
+
+;;;
;;; hashx-remove!
;;;
(with-test-prefix "hashx-remove!"