Hash maps #
Defines a hash map data structure, representing a finite key-value map
with a value type that may depend on the key type. The structure
requires a nat-valued hash function to associate keys to buckets.
Main definitions #
hash_map: constructed withmk_hash_map.
Implementation details #
A hash map with key type α and (dependent) value type β : α → Type*
consists of an array of buckets, which are lists containing
key/value pairs for that bucket. The hash function is taken modulo n
to assign keys to their respective bucket. Because of this, some care
should be put into the hash function to ensure it evenly distributes
keys.
The bucket array is an array. These have special VM support for
in-place modification if there is only ever one reference to them. If
one takes special care to never keep references to old versions of a
hash map alive after updating it, then the hash map will be modified
in-place. In this documentation, when we say a hash map is modified
in-place, we are assuming the API is being used in this manner.
When inserting (hash_map.insert), if the number of stored pairs (the
size) is going to exceed the number of buckets, then a new hash map
is first created with double the number of buckets and everything in
the old hash map is reinserted along with the new key/value pair.
Otherwise, the bucket array is modified in-place. The amortized
running time of inserting $$n$$ elements into a hash map is $$O(n)$$.
When removing (hash_map.erase), the hash map is modified in-place.
The implementation does not reduce the number of buckets in the hash
map if the size gets too low.
Tags #
hash map
bucket_array α β is the underlying data type for hash_map α β,
an array of linked lists of key-value pairs.
Equations
- bucket_array α β n = array ↑n (list (Σ (a : α), β a))
Instances for bucket_array
Read the bucket corresponding to an element
Equations
- bucket_array.read hash_fn data a = let bidx : fin ↑n := hash_map.mk_idx n (hash_fn a) in array.read data bidx
Write the bucket corresponding to an element
Equations
- bucket_array.write hash_fn data a l = let bidx : fin ↑n := hash_map.mk_idx n (hash_fn a) in array.write data bidx l
Modify (read, apply f, and write) the bucket corresponding to an element
Equations
- bucket_array.modify hash_fn data a f = let bidx : fin ↑n := hash_map.mk_idx n (hash_fn a) in array.write data bidx (f (array.read data bidx))
The list of all key-value pairs in the bucket list
Equations
- data.as_list = (array.to_list data).join
Fold a function f over the key-value pairs in the bucket list
Equations
- data.foldl d f = array.foldl data d (λ (b : list (Σ (a : α), β a)) (d : δ), list.foldl (λ (r : δ) (a : Σ (a : α), β a), f r a.fst a.snd) d b)
Insert the pair ⟨a, b⟩ into the correct location in the bucket array
(without checking for duplication)
Equations
- hash_map.reinsert_aux hash_fn data a b = bucket_array.modify hash_fn data a (λ (l : list (Σ (a : α), β a)), ⟨a, b⟩ :: l)
Search a bucket for a key a and return the value
Equations
- hash_map.find_aux a (⟨a', b⟩ :: t) = dite (a' = a) (λ (h : a' = a), option.some (h.rec_on b)) (λ (h : ¬a' = a), hash_map.find_aux a t)
- hash_map.find_aux a list.nil = option.none
Returns tt if the bucket l contains the key a
Equations
- hash_map.contains_aux a l = (hash_map.find_aux a l).is_some
Modify a bucket to replace a value in the list. Leaves the list unchanged if the key is not found.
Equations
- hash_map.replace_aux a b (⟨a', b'⟩ :: t) = ite (a' = a) (⟨a, b⟩ :: t) (⟨a', b'⟩ :: hash_map.replace_aux a b t)
- hash_map.replace_aux a b list.nil = list.nil
Modify a bucket to remove a key, if it exists.
Equations
- hash_map.erase_aux a (⟨a', b'⟩ :: t) = ite (a' = a) t (⟨a', b'⟩ :: hash_map.erase_aux a t)
- hash_map.erase_aux a list.nil = list.nil
- len : bkts.as_list.length = sz
- idx : ∀ {i : fin ↑n} {a : Σ (a : α), β a}, a ∈ array.read bkts i → hash_map.mk_idx n (hash_fn a.fst) = i
- nodup : ∀ (i : fin ↑n), (list.map sigma.fst (array.read bkts i)).nodup
The predicate valid bkts sz means that bkts satisfies the hash_map
invariants: There are exactly sz elements in it, every pair is in the
bucket determined by its key and the hash function, and no key appears
multiple times in the list.
- hash_fn : α → ℕ
- size : ℕ
- nbuckets : ℕ+
- buckets : bucket_array α β self.nbuckets
- is_valid : hash_map.valid self.hash_fn self.buckets self.size
A hash map data structure, representing a finite key-value map
with key type α and value type β (which may depend on α).
Instances for hash_map
- hash_map.has_sizeof_inst
- hash_map.has_mem
- hash_map.has_to_string
- hash_map.has_to_format
- hash_map.inhabited
Construct an empty hash map with buffer size nbuckets (default 8).
Return the value corresponding to a key, or none if not found
Equations
- m.find a = hash_map.find_aux a (bucket_array.read m.hash_fn m.buckets a)
Return tt if the key exists in the map
Fold a function over the key-value pairs in the map
The list of key-value pairs in the map
The list of keys in the map
Insert a key-value pair into the map. (Modifies m in-place when applicable)
Equations
- {hash_fn := hash_fn, size := size, nbuckets := n, buckets := buckets, is_valid := v}.insert a b = let bkt : list (Σ (a : α), β a) := bucket_array.read hash_fn buckets a in dite ↥(hash_map.contains_aux a bkt) (λ (hc : ↥(hash_map.contains_aux a bkt)), {hash_fn := hash_fn, size := size, nbuckets := n, buckets := bucket_array.modify hash_fn buckets a (hash_map.replace_aux a b), is_valid := _}) (λ (hc : ¬↥(hash_map.contains_aux a bkt)), let size' : ℕ := size + 1, buckets' : bucket_array α β n := bucket_array.modify hash_fn buckets a (λ (l : list (Σ (a : α), β a)), ⟨a, b⟩ :: l), valid' : hash_map.valid hash_fn (hash_map.reinsert_aux hash_fn buckets a b) (size + 1) := _ in ite (size' ≤ ↑n) {hash_fn := hash_fn, size := size', nbuckets := n, buckets := buckets', is_valid := valid'} (let n' : ℕ+ := ⟨↑n * 2, _⟩, buckets'' : bucket_array α β n' := buckets'.foldl (mk_array ↑n' list.nil) (hash_map.reinsert_aux hash_fn) in {hash_fn := hash_fn, size := size', nbuckets := n', buckets := buckets'', is_valid := _}))
Insert a list of key-value pairs into the map. (Modifies m in-place when applicable)
Equations
- hash_map.insert_all l m = list.foldl (λ (m : hash_map α β) (_x : Σ (a : α), β a), hash_map.insert_all._match_1 m _x) m l
- hash_map.insert_all._match_1 m ⟨a, b⟩ = m.insert a b
Construct a hash map from a list of key-value pairs.
Equations
- hash_map.of_list l hash_fn = hash_map.insert_all l (mk_hash_map hash_fn (2 * l.length))
Remove a key from the map. (Modifies m in-place when applicable)
Equations
- m.erase a = hash_map.erase._match_1 m a m
- hash_map.erase._match_1 m a {hash_fn := hash_fn, size := size, nbuckets := n, buckets := buckets, is_valid := v} = dite ↥(hash_map.contains_aux a (bucket_array.read hash_fn buckets a)) (λ (hc : ↥(hash_map.contains_aux a (bucket_array.read hash_fn buckets a))), {hash_fn := hash_fn, size := size - 1, nbuckets := n, buckets := bucket_array.modify hash_fn buckets a (hash_map.erase_aux a), is_valid := _}) (λ (hc : ¬↥(hash_map.contains_aux a (bucket_array.read hash_fn buckets a))), m)
Equations
- hash_map.has_to_string = {to_string := to_string (λ (a : α), _inst_3 a)}
hash_map with key type nat and value type that may vary.
Equations
- hash_map.inhabited = {default := (mk_hash_map id)}