While examining ways to refactor the PFP library, it struck me that its strategy of storing repeated values and dealing with displaying them as an aggregate later was somewhat generalizable. This is a first stab at a counter datatype with constant-time update and (probably) O(m) lookup — where m is the sum of counts. The alternate strategy, using a Map, has O(log(n)) lookup and update time — where n is the number of counters.
What do we want to do here?
module Counter (count, inc, dec, empty, fromCounter, toCounter) where
We want to store a list of values together with counts, e.g.
*Counter> example
"Rock" 18
"Jazz" 27
"Classical" 13
" Etc." 35
We should be able to retrieve any count
*Counter> count "Rock" example
18
We want cheap inc
*Counter> inc "Rock" example
"Rock" 19
"Jazz" 27
"Classical" 13
" Etc." 35
inc should handle gracefully (and still cheaply) new values:
*Counter> inc "Traditional" example
"Traditional" 1
"Rock" 18
"Jazz" 27
"Classical" 13
" Etc." 35
There should be a functioning dec that also handles invalid cases gracefully:
*Counter> dec "Classical" example
"Rock" 18
"Jazz" 27
"Classical" 12
" Etc." 35
*Counter> dec "Mbembe" example
"Rock" 18
"Jazz" 27
"Classical" 13
" Etc." 35
There should be a simply empty counter so new counters can be constructed via inc chains:
*Counter> inc False $ inc True $ inc False $ inc True empty
False 2
True 2
Finally, there should be convenience functions to construct a counter from a list of (key, count) pairs
*Counter> toCounter [("Bjork",15),("Gentle Giant",12)]
“Bjork” 15
“Gentle Giant” 12
and to destruct a counter into such a list
*Counter> fromCounter example
[("Rock",18),("Jazz",27),("Classical",13),(" Etc.",35)]
Here’s how we implement it.
A counter is merely a list of values.
{
℘ continues here. }
{ 680 words, estimated 2:43 mins reading time)}