hash.rkt: Add hash-intersect
This commit is contained in:
parent
9905c1c89a
commit
5cbff3403a
|
@ -758,4 +758,41 @@ h
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@defproc[(hash-intersect [h0 (and/c hash? (not/c immutable?))]
|
||||||
|
[h hash?] ...
|
||||||
|
[#:combine combine
|
||||||
|
(-> any/c any/c any/c)
|
||||||
|
(lambda _ (error 'hash-intersect ...))]
|
||||||
|
[#:combine/key combine/key
|
||||||
|
(-> any/c any/c any/c any/c)
|
||||||
|
(lambda (k a b) (combine a b))])
|
||||||
|
(and/c hash? immutable?)] {
|
||||||
|
|
||||||
|
Constructs the hash table which is the intersection of @racket[h0]
|
||||||
|
with every hash table @racket[h]. In the resulting hash table, a key
|
||||||
|
@racket[k] is mapped to a combination of the values to which
|
||||||
|
@racket[k] is mapped in each of the hash tables. The final values are
|
||||||
|
computed by stepwise combination of the values appearing in each of
|
||||||
|
the hash tables by applying @racket[(combine/key k v vi)] or
|
||||||
|
@racket[(combine v vi)], where @racket[vi] is the value to which
|
||||||
|
@racket[k] is mapped in the i-th hash table @racket[h], and
|
||||||
|
@racket[v] is the accumulation of the values from the previous steps.
|
||||||
|
The comparison predicate of the first argument (@racket[eq?],
|
||||||
|
@racket[eqv?], @racket[equal?]) determines the one for the result.
|
||||||
|
|
||||||
|
@examples[
|
||||||
|
#:eval the-eval
|
||||||
|
(hash-intersect (make-immutable-hash '((a . 1) (b . 2) (c . 3)))
|
||||||
|
(make-immutable-hash '((a . 4) (b . 5)))
|
||||||
|
#:combine +)
|
||||||
|
(hash-intersect (make-immutable-hash '((a . 1) (b . 2) (c . 3)))
|
||||||
|
(make-immutable-hash '((a . 4) (b . 5)))
|
||||||
|
#:combine/key
|
||||||
|
(lambda (k v1 v2) (if (eq? k 'a) (+ v1 v2) (- v1 v2))))
|
||||||
|
]
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
@(close-eval the-eval)
|
@(close-eval the-eval)
|
||||||
|
|
||||||
|
@history[#:added "7.8.0.11"]
|
||||||
|
|
|
@ -65,6 +65,20 @@
|
||||||
hash-union #hash([one . 1] [two . 1]) #hash([three . 3] [four . 4] [two . 1])
|
hash-union #hash([one . 1] [two . 1]) #hash([three . 3] [four . 4] [two . 1])
|
||||||
#:combine +)
|
#:combine +)
|
||||||
|
|
||||||
|
(test #hash((a . 5) (b . 7))
|
||||||
|
hash-intersect #hash((a . 1) (b . 2) (c . 3)) #hash((a . 4) (b . 5))
|
||||||
|
#:combine +)
|
||||||
|
(test #hash((a . 5) (b . -3))
|
||||||
|
hash-intersect #hash((a . 1) (b . 2) (c . 3)) #hash((a . 4) (b . 5))
|
||||||
|
#:combine/key
|
||||||
|
(lambda (k v1 v2) (if (eq? k 'a) (+ v1 v2) (- v1 v2))))
|
||||||
|
|
||||||
|
;; Does hash-intersect preserve the kind of the hash?
|
||||||
|
(test (hasheq "a" 11)
|
||||||
|
hash-intersect (hasheq "a" 1 (string #\a) 2 (string #\a) 3)
|
||||||
|
(hasheq "a" 10 (string #\a) 20)
|
||||||
|
#:combine +)
|
||||||
|
|
||||||
(let ()
|
(let ()
|
||||||
(define h (make-hash))
|
(define h (make-hash))
|
||||||
(hash-union! h #hash([1 . one] [2 . two]))
|
(hash-union! h #hash([1 . one] [2 . two]))
|
||||||
|
|
|
@ -28,6 +28,31 @@
|
||||||
(combine/key k (hash-ref one k) v)
|
(combine/key k (hash-ref one k) v)
|
||||||
v))))
|
v))))
|
||||||
|
|
||||||
|
(define (hash-intersect
|
||||||
|
#:combine [combine #f]
|
||||||
|
#:combine/key [combine/key
|
||||||
|
(if combine
|
||||||
|
(λ (_ x y) (combine x y))
|
||||||
|
(hash-duplicate-error 'hash-intersect))]
|
||||||
|
. hashes)
|
||||||
|
(define one (car hashes))
|
||||||
|
(define rest (cdr hashes))
|
||||||
|
(define empty-h (hash-clear one)) ;; empty hash of same type as one
|
||||||
|
(define (argmin f lst) ;; avoid racket/list to improve loading time
|
||||||
|
(for/fold ([best (car lst)] [fbest (f (car lst))]
|
||||||
|
#:result best)
|
||||||
|
([x lst])
|
||||||
|
(define fx (f x))
|
||||||
|
(if (< fx fbest) (values x fx) (values best fbest))))
|
||||||
|
(for/fold ([res empty-h])
|
||||||
|
([k (in-hash-keys (argmin hash-count hashes))])
|
||||||
|
(if (for/and ([h (in-list hashes)]) (hash-has-key? h k))
|
||||||
|
(hash-set res k
|
||||||
|
(for/fold ([v (hash-ref one k)])
|
||||||
|
([hm (in-list rest)])
|
||||||
|
(combine/key k v (hash-ref hm k))))
|
||||||
|
res)))
|
||||||
|
|
||||||
(provide/contract
|
(provide/contract
|
||||||
[hash-union (->* [(and/c hash? immutable?)]
|
[hash-union (->* [(and/c hash? immutable?)]
|
||||||
[#:combine
|
[#:combine
|
||||||
|
@ -42,4 +67,11 @@
|
||||||
#:combine/key
|
#:combine/key
|
||||||
(-> any/c any/c any/c any/c)]
|
(-> any/c any/c any/c any/c)]
|
||||||
#:rest (listof hash?)
|
#:rest (listof hash?)
|
||||||
void?)])
|
void?)]
|
||||||
|
[hash-intersect (->* [(and/c hash? immutable?)]
|
||||||
|
[#:combine
|
||||||
|
(-> any/c any/c any/c)
|
||||||
|
#:combine/key
|
||||||
|
(-> any/c any/c any/c any/c)]
|
||||||
|
#:rest (listof hash?)
|
||||||
|
(and/c hash? immutable?))])
|
||||||
|
|
Loading…
Reference in New Issue
Block a user