💾 Archived View for idiomdrottning.org › call-table captured on 2022-06-03 at 23:13:40. Gemini links have been rewritten to link to archived content
⬅️ Previous capture (2021-12-03)
➡️ Next capture (2022-07-16)
-=-=-=-=-=-=-
A more convenient way to use hash tables.
A call-table is a lexically closed function wrapping a hash table.
Calling it with two arguments sets a key to that value.
(define flavor (call-table)) (flavor 'red 'strawberry) (flavor 'yellow 'lemon) (flavor 'green 'lemon-balm)
Calling it with one argument retrieves the value for that key.
(list (flavor 'red) (flavor 'yellow) (flavor 'green))
⇒ (strawberry lemon lemon-balm)
You can change your mind and update existing keys:
(flavor 'green 'parsley) (flavor 'green)
⇒ parsley
The default value for unknown keys is #f.
(flavor 'chartreuse)
⇒ #f
You can set another default value when initially creating the call-value.
(define ghosts-in-room (call-table default: 0)) (ghosts-in-room 'attic 3) (ghosts-in-room 'basement 2) (apply + (map ghosts-in-room '(kitchen bedroom attic basement patio)))
⇒ 5
Abstraction is great when it helps us. We don’t want to put too many hoops in our own way.
You can access the underlying hash table by calling the call-table without any arguments.
(hash-table? (flavor))
⇒ #t
You can swap out the underlying hash table by using the keyword update: and a hash table. This is currently the only key with a special meaning.
(flavor update: ((as-list (over (pair) (cons (cdr pair) (car pair)))) (flavor))) (list (flavor 'strawberry) (flavor 'lemon) (flavor 'parsley))
⇒ (red yellow green)
It specifically only listens for the combination of the keyword update: and the value type hash-table. Other key/value pairs aren’t treated specially.
(flavor update: "a string") (flavor update:)
⇒ “a string”
The vanilla call-table replaces its values. Sometimes, you instead want to keep accumulating new values.
(define flavor (call-table*)) (flavor 'red 'strawberry) (flavor 'blue 'blueberry) (flavor 'red 'raspberry) (list (flavor 'blue) (flavor 'red))
⇒ ((blueberry) (raspberry strawberry))
The default is to cons the new values onto a list of the old ones.
You can change that:
(define ghosts-in-room (call-table* proc: + initial: 0)) (ghosts-in-room 'attic 3) (ghosts-in-room 'basement 2) (ghosts-in-room 'attic 1) (map ghosts-in-room '(kitchen bedroom attic basement patio))
⇒ (0 0 4 2 0)
Sometimes, it’s even useful to just have a unary procedure.
(define ghost-counter (call-table* proc: add1 initial: 0 unary: #t)) (ghost-counter 'garden) (ghost-counter 'gazebo) (ghost-counter 'garden)
With unary procedures, while each call returns the new value (which can often be useful enough), there’s no read-only idempotent way to just retrieve values unless you access the entire hash table.
(hash-table->alist (ghost-counter))
⇒ ((garden . 2) (gazebo . 1))
But that’s where our under-the-hood access can come in handy:
(ghosts-in-room update: ((as-list append) (ghosts-in-room) (ghost-counter))) (hash-table-for-each (ghosts-in-room) (lambda (room amount) (print "There were " amount " ghosts in the " room ".")))
There were 4 ghosts in the attic.
There were 1 ghosts in the gazebo.
There were 2 ghosts in the garden.
There were 2 ghosts in the basement.
You can also use set! to set slots.
On a call-table*, the new values are added to the accumulators, rather than replacing them.
(set! (flavor 'red) 'watermelon)
⇒ (watermelon raspberry strawberry)
You can also swap out the underlying hash-table.
(set! (flavor) (make-hash-table))
You can include an alist or a hash-table while first creating your call-table.
(define horse (call-table seed: '((epona . zelda) (sundance . g1) (stridor . motu)))) (horse 'epona) (horse 'hero 'phantom) (horse 'hero)
Sometimes you think call-table is convenient but you only need one key.
For call-key, just use make-parameter.
But call-key* is awesome since it accumulates its values.
It has the same proc, unary, and initial keyword arguments as call-table*. It doesn’t have seed because the idea is that you just use inititial. The generated procedure has update (which takes a new list as argument) and get (which you only need for unary call-keys).
(define horses (call-key*)) (horses 'ruby) (horses 'kind-girl) (horses 'tornado) (horses)
⇒ (tornado kind-girl ruby)
call-table and call-table* are part of brev-separate.
git clone https://idiomdrottning.org/brev-separate