spec: mutable hash tables (make-hash-table/ref/set!/delete!/etc)
Phase 10 — 11 primitives: make-hash-table, hash-table?, hash-table-set!, hash-table-ref, hash-table-delete!, hash-table-size, hash-table-keys, hash-table-values, hash-table->alist, hash-table-for-each, hash-table-merge!. OCaml HashTable variant; JS Map-based. 28 tests, both hosts green. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -849,3 +849,5 @@
|
||||
:params ((a :as number))
|
||||
:returns "number"
|
||||
:doc "Number of bits needed to represent integer a (excluding sign).")
|
||||
|
||||
(define-module :stdlib.hash-table)
|
||||
|
||||
166
spec/tests/test-hash-table.sx
Normal file
166
spec/tests/test-hash-table.sx
Normal file
@@ -0,0 +1,166 @@
|
||||
;; Tests for mutable hash tables (Phase 10)
|
||||
|
||||
(defsuite
|
||||
"hash-table"
|
||||
(deftest
|
||||
"make-hash-table returns a hash table"
|
||||
(assert (hash-table? (make-hash-table))))
|
||||
(deftest
|
||||
"hash-table? false for dict"
|
||||
(assert= false (hash-table? {:a 1})))
|
||||
(deftest "hash-table? false for nil" (assert= false (hash-table? nil)))
|
||||
(deftest
|
||||
"hash-table? false for list"
|
||||
(assert= false (hash-table? (list 1 2))))
|
||||
(deftest
|
||||
"empty table has size 0"
|
||||
(assert= 0 (hash-table-size (make-hash-table))))
|
||||
(deftest
|
||||
"size after one set"
|
||||
(let
|
||||
((ht (make-hash-table)))
|
||||
(hash-table-set! ht "a" 1)
|
||||
(assert= 1 (hash-table-size ht))))
|
||||
(deftest
|
||||
"size after multiple sets"
|
||||
(let
|
||||
((ht (make-hash-table)))
|
||||
(hash-table-set! ht "a" 1)
|
||||
(hash-table-set! ht "b" 2)
|
||||
(hash-table-set! ht "c" 3)
|
||||
(assert= 3 (hash-table-size ht))))
|
||||
(deftest
|
||||
"set same key does not grow size"
|
||||
(let
|
||||
((ht (make-hash-table)))
|
||||
(hash-table-set! ht "a" 1)
|
||||
(hash-table-set! ht "a" 2)
|
||||
(assert= 1 (hash-table-size ht))))
|
||||
(deftest
|
||||
"ref returns set value"
|
||||
(let
|
||||
((ht (make-hash-table)))
|
||||
(hash-table-set! ht "k" 42)
|
||||
(assert= 42 (hash-table-ref ht "k"))))
|
||||
(deftest
|
||||
"ref returns updated value after overwrite"
|
||||
(let
|
||||
((ht (make-hash-table)))
|
||||
(hash-table-set! ht "k" 1)
|
||||
(hash-table-set! ht "k" 99)
|
||||
(assert= 99 (hash-table-ref ht "k"))))
|
||||
(deftest
|
||||
"ref with default returns default for missing key"
|
||||
(assert=
|
||||
"fallback"
|
||||
(hash-table-ref (make-hash-table) "missing" "fallback")))
|
||||
(deftest
|
||||
"ref with default returns value when key exists"
|
||||
(let
|
||||
((ht (make-hash-table)))
|
||||
(hash-table-set! ht "x" 7)
|
||||
(assert= 7 (hash-table-ref ht "x" 0))))
|
||||
(deftest
|
||||
"keyword keys work"
|
||||
(let
|
||||
((ht (make-hash-table)))
|
||||
(hash-table-set! ht :foo "bar")
|
||||
(assert= "bar" (hash-table-ref ht :foo))))
|
||||
(deftest
|
||||
"number keys work"
|
||||
(let
|
||||
((ht (make-hash-table)))
|
||||
(hash-table-set! ht 0 "zero")
|
||||
(assert= "zero" (hash-table-ref ht 0))))
|
||||
(deftest
|
||||
"delete removes key"
|
||||
(let
|
||||
((ht (make-hash-table)))
|
||||
(hash-table-set! ht "x" 1)
|
||||
(hash-table-delete! ht "x")
|
||||
(assert= "gone" (hash-table-ref ht "x" "gone"))))
|
||||
(deftest
|
||||
"delete reduces size"
|
||||
(let
|
||||
((ht (make-hash-table)))
|
||||
(hash-table-set! ht "a" 1)
|
||||
(hash-table-set! ht "b" 2)
|
||||
(hash-table-delete! ht "a")
|
||||
(assert= 1 (hash-table-size ht))))
|
||||
(deftest
|
||||
"delete missing key is no-op"
|
||||
(let
|
||||
((ht (make-hash-table)))
|
||||
(hash-table-delete! ht "absent")
|
||||
(assert= 0 (hash-table-size ht))))
|
||||
(deftest
|
||||
"keys of empty table is empty"
|
||||
(assert (empty? (hash-table-keys (make-hash-table)))))
|
||||
(deftest
|
||||
"keys has correct count"
|
||||
(let
|
||||
((ht (make-hash-table)))
|
||||
(hash-table-set! ht "a" 1)
|
||||
(hash-table-set! ht "b" 2)
|
||||
(assert= 2 (len (hash-table-keys ht)))))
|
||||
(deftest
|
||||
"values has correct count"
|
||||
(let
|
||||
((ht (make-hash-table)))
|
||||
(hash-table-set! ht "a" 10)
|
||||
(hash-table-set! ht "b" 20)
|
||||
(assert= 2 (len (hash-table-values ht)))))
|
||||
(deftest
|
||||
"alist of empty table is empty"
|
||||
(assert (empty? (hash-table->alist (make-hash-table)))))
|
||||
(deftest
|
||||
"alist has correct length"
|
||||
(let
|
||||
((ht (make-hash-table)))
|
||||
(hash-table-set! ht "x" 1)
|
||||
(hash-table-set! ht "y" 2)
|
||||
(assert= 2 (len (hash-table->alist ht)))))
|
||||
(deftest
|
||||
"for-each visits all entries"
|
||||
(let
|
||||
((ht (make-hash-table)) (count 0))
|
||||
(hash-table-set! ht "a" 1)
|
||||
(hash-table-set! ht "b" 2)
|
||||
(hash-table-set! ht "c" 3)
|
||||
(hash-table-for-each ht (fn (k v) (set! count (+ count 1))))
|
||||
(assert= 3 count)))
|
||||
(deftest
|
||||
"for-each sums values"
|
||||
(let
|
||||
((ht (make-hash-table)) (total 0))
|
||||
(hash-table-set! ht "a" 10)
|
||||
(hash-table-set! ht "b" 20)
|
||||
(hash-table-set! ht "c" 30)
|
||||
(hash-table-for-each ht (fn (k v) (set! total (+ total v))))
|
||||
(assert= 60 total)))
|
||||
(deftest
|
||||
"merge copies entries from src to dst"
|
||||
(let
|
||||
((dst (make-hash-table)) (src (make-hash-table)))
|
||||
(hash-table-set! src "x" 1)
|
||||
(hash-table-set! src "y" 2)
|
||||
(hash-table-merge! dst src)
|
||||
(assert= 2 (hash-table-size dst))))
|
||||
(deftest
|
||||
"merge overwrites existing keys in dst"
|
||||
(let
|
||||
((dst (make-hash-table)) (src (make-hash-table)))
|
||||
(hash-table-set! dst "k" "old")
|
||||
(hash-table-set! src "k" "new")
|
||||
(hash-table-merge! dst src)
|
||||
(assert= "new" (hash-table-ref dst "k"))))
|
||||
(deftest
|
||||
"merge does not modify src"
|
||||
(let
|
||||
((dst (make-hash-table)) (src (make-hash-table)))
|
||||
(hash-table-set! src "a" 1)
|
||||
(hash-table-merge! dst src)
|
||||
(assert= 1 (hash-table-size src))))
|
||||
(deftest
|
||||
"type-of returns hash-table"
|
||||
(assert= "hash-table" (type-of (make-hash-table)))))
|
||||
Reference in New Issue
Block a user