multisetdemo.gno
2.82 Kb · 73 lines
1// Package multisetdemo is a small gnoweb demo of the bag / frequency counter
2// provided by the [p/moul/x/daily/multiset](/p/moul/x/daily/multiset/v0)
3// library: word frequencies, ranking, and multiset algebra.
4//
5// It contains no counting logic of its own. Stateless, so Render is
6// deterministic — which is precisely what the library is for.
7package multisetdemo
8
9import (
10 "strconv"
11 "strings"
12
13 "gno.land/p/moul/x/daily/multiset/v0"
14)
15
16const sample = "the quick brown fox jumps over the lazy dog the fox barks and the dog barks"
17
18// Render renders the demo for gnoweb.
19func Render(path string) string {
20 var b strings.Builder
21 b.WriteString("# Multiset\n\n")
22 b.WriteString("A bag that counts duplicates, demoing the ")
23 b.WriteString("[`p/moul/x/daily/multiset`](/p/moul/x/daily/multiset/v0) library.\n\n")
24
25 m := multiset.FromSlice(strings.Split(sample, " "))
26
27 b.WriteString("## Word frequencies\n\n")
28 b.WriteString("> " + sample + "\n\n")
29 b.WriteString("`" + strconv.Itoa(m.Total()) + "` words, `")
30 b.WriteString(strconv.Itoa(m.Distinct()) + "` distinct.\n\n")
31
32 b.WriteString("## `MostCommon(5)`\n\n")
33 b.WriteString("| rank | word | count |\n|---|---|---|\n")
34 for i, e := range m.MostCommon(5) {
35 b.WriteString("| " + strconv.Itoa(i+1) + " | `" + e.Elem + "` | " +
36 strconv.Itoa(e.Count) + " |\n")
37 }
38 b.WriteString("\nNote the ties. `barks`, `dog` and `fox` all occur twice and are ")
39 b.WriteString("ranked **alphabetically** — the order is count descending, then ")
40 b.WriteString("element ascending. That second key is not decoration: without it the ")
41 b.WriteString("ranking would fall back on map iteration order, which gno leaves ")
42 b.WriteString("unspecified, and two nodes could render different tables.\n\n")
43
44 b.WriteString("## Algebra\n\n")
45 a := multiset.FromSlice([]string{"x", "x", "x", "y"})
46 c := multiset.FromSlice([]string{"x", "x", "z"})
47 b.WriteString("With `A = {x:3, y:1}` and `B = {x:2, z:1}`:\n\n")
48 b.WriteString("| op | meaning | result |\n|---|---|---|\n")
49 b.WriteString("| `Union` | max of each count | " + brief(a.Union(c)) + " |\n")
50 b.WriteString("| `Intersect` | min, common only | " + brief(a.Intersect(c)) + " |\n")
51 b.WriteString("| `Sum` | counts added | " + brief(a.Sum(c)) + " |\n")
52
53 b.WriteString("\n## Removal\n\n")
54 d := multiset.FromSlice([]string{"a", "a", "a"})
55 b.WriteString("Starting from " + brief(d) + ":\n\n")
56 d.Remove("a")
57 b.WriteString("- after `Remove(\"a\")` → " + brief(d) + "\n")
58 d.RemoveN("a", 100)
59 b.WriteString("- after `RemoveN(\"a\", 100)` → " + brief(d) +
60 " — over-removing clears the element instead of going negative\n")
61 return b.String()
62}
63
64func brief(m *multiset.MultiSet) string {
65 if m.IsEmpty() {
66 return "`{}`"
67 }
68 parts := []string{}
69 for _, e := range m.Elements() {
70 parts = append(parts, e+":"+strconv.Itoa(m.Count(e)))
71 }
72 return "`{" + strings.Join(parts, ", ") + "}`"
73}