This is the mail archive of the guile@sourceware.cygnus.com mailing list for the Guile project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]

Re: how to work with (weak) hash tables?


Valentin Kamyshenko <val@kamysh.materials.kiev.ua> writes:

> >>>>> "Greg" == Greg Harvey <Greg.Harvey@thezone.net> writes:
> 
> Greg> (define (hash-for-each proc hash) (let ((len (vector-length
> Greg> hash))) (do ((i 0 (+ i 1))) ((= i len) ()) (for-each proc
> Greg> (vector-ref hash i)))))
> 
> 
> guile> (define x (make-hash-table 10))
> Greg> ; Prime, shmime ;)
> guile> (hashq-set! x 'a 1)
> Greg> 1
> g1uile> (hashq-set! x 'b 1)
> Greg> 1
> guile> (hash-for-each (lambda (x) (set-cdr! x (+ (cdr x) 1))) x)
> Greg> ()
> guile> x
> Greg> #(((a . 2)) () () () ((b . 2)) () () () () ())
> 
> Wow, it is surprisingly easy to corrupt everything:

That it is; you've stuck 'a in a place where it can't possibly be
found by the hash function. There's nothing in here to prevent you
from doing outragously stu^H^H^H incorrect things >;').

> guile>	(hash-for-each 
> ...		(lambda (x) 
> ...			(set-car! x 
> ...				(if (eq? (car x) 'a) 'b 'a))) x)
> 
> guile> x
> #(((b . 2)) () () () ((a . 2)) () () () () ())
> guile> (hash-get-handle x 'a)
> #f
> guile> (hash-get-handle x 'b)
> #f
> 
> Can not say, I like this...
> 
> What do you think about adding a function `hash-for-each-value!' to
> hashtab.c (with evident behavior) to exclude such possibilities, and 
> to be independent on current realization of hashtables? 

Again, this runs into problems with the current representation (and
any other foreseeable implementation). In the case of immediates, you
have (in whatever position a hashes to) '((a . 1) ...); the 1 is not a
cons cell, so if you just get the value, you can't set it and have it
change in the actual cons, and you can't put it in a cons cell,
because if someone wants to set a value to a cons with an immediate at
the start, you'd end up returning just that immediate whenever they
asked for the value back, which is just as wrong. Basically, it's do
it the clean way (hash-fold) or the messy way where you can easily
take the whole foot off if you aren't careful. 

That's not to say it's not possible to have a hash-for-each-value!;
basically, you need to have hash-value and set-hash-value! to modify
the current hash value (so, you need a way to get them into the
environment of proc; maybe with convoluted macros), but it's just much
easier to do it one of the other ways.

A slightly safer hash-for-each
(define-macro (hash-for-each proc hash)
  (let ((len (gensym))
        (real-list-set (gensym)))
    `(let* ((,len (vector-length ,hash))
            (set-car! (lambda (x y) (error "sorry, can't set the car down here"))))
       
       (do ((i 0 (+ i 1)))
           ((= i ,len) ())
         (for-each ,proc (vector-ref ,hash i))))))

Note that, you also should remove list-set! foo blah 0, etc, and you
also can't map hash-for-each over anything.

This is one way you could get a hash-for-each-value!; it requires that
the proc is a lambda form (I know it's disgusting; that's the whole
problem ;).

(define-macro (hash-for-each-value! proc hash)
  (let ((len (gensym))
        (src proc)
        (newproc ())
        (newprocarg (gensym)))
    (cond ((not src) (error "need a procedure with source" proc))
          ((not (eq? (car src) 'lambda)) (error "need a lambda form" proc))
          (else (set! newproc `(lambda (,newprocarg)
                           (let ((set-hash-value! (lambda (x) (set-cdr! ,newprocarg x)))
                                 (hash-value (cdr ,newprocarg)))
                             ,@(cddr src))))))
    `(let* ((,len (vector-length ,hash)))
       (do ((i 0 (+ i 1)))
           ((= i ,len) ())
         (for-each ,newproc (vector-ref ,hash i))))))



guile> x
#(((a . 5)) () () () ((b . 5)) () () () () ())
guile> (hash-for-each-value! (lambda () (set-hash-value! (+ hash-value 1))) x)
()
guile> x
#(((a . 6)) () () () ((b . 6)) () () () () ())

Of course, chances are that you'll end up screwing around and consing
enough in doing something like this to make it much more efficient to
just do a (hashq-set! hash key val).

-- 
Greg

Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]