Check several values in map and query, if missed, using continuations.
authorsgf <sgf.dma@gmail.com>
Thu, 1 Jun 2023 18:24:50 +0000 (21:24 +0300)
committersgf <sgf.dma@gmail.com>
Thu, 1 Jun 2023 18:24:50 +0000 (21:24 +0300)
cont.go [new file with mode: 0644]

diff --git a/cont.go b/cont.go
new file mode 100644 (file)
index 0000000..ece24a0
--- /dev/null
+++ b/cont.go
@@ -0,0 +1,206 @@
+
+package main
+
+import (
+    "fmt"
+)
+
+// - Function fillMap() fills the map.
+// - And fillMap() should run only once for continuation chain.
+//
+// - Function h() checks the value x in the map. If it's missed, it tries to
+// call fillMap() (if it wasn't already called) and checks the value again.
+// - If h() finds the value (either at first or second try) it calls the next
+// step.
+
+var m1 map[int]string
+var queried1 bool
+
+func fillMap1() {
+    fmt.Printf("Filling map1\n")
+    queried1 = true
+    if m1 == nil {
+        m1 = make(map[int]string)
+    }
+    m1[1] = "111"
+    m1[2] = "222"
+    m1[3] = "333"
+}
+
+type Q func() (Q, error)
+
+// Use global 'queried1' state.
+func h1(x int, k Q) (Q, error) {
+    z, ok := m1[x]
+    if !ok {
+        fmt.Printf("h(%v): Missed key %v. Refill map.\n", x, x)
+        if queried1 {
+            return nil, fmt.Errorf("h(%v): No such key %v", x, x)
+        }
+        fillMap1()
+        return func() (Q, error) { return h1(x, k) }, nil
+    }
+    fmt.Printf("h(%v): Obtained %v\n", x, z)
+    return k, nil
+}
+
+func run1() {
+    var err error
+
+    fmt.Printf("========== Start1\n")
+
+    g3 := func () (Q, error) { return h1(3, nil) }
+    g2 := func () (Q, error) { return h1(2, g3) }
+    g1 := func () (Q, error) { return h1(1, g2) }
+    fmt.Printf("g1 = %v, g2 = %v, g3 = %v\n", g1, g2, g3)
+    k := g1
+    for k != nil {
+        fmt.Printf("Calling %v\n", k)
+        k, err = k()
+        if err != nil {
+            fmt.Printf("Received error: %v\n", err)
+            break
+        }
+    }
+    fmt.Printf("End1\n")
+}
+
+
+var m2 map[int]string
+
+func fillMap2() {
+    fmt.Printf("Filling map2\n")
+    if m2 == nil {
+        m2 = make(map[int]string)
+    }
+    m2[1] = "111"
+    m2[2] = "222"
+    m2[3] = "333"
+}
+
+// Pass "queried" state internally inside continuations chain.
+func h2(x int, k func(bool) Q, queried bool) (Q, error) {
+    z, ok := m2[x]
+    if !ok {
+        fmt.Printf("h(%v): Missed key %v.\n", x, x)
+        if queried {
+            return nil, fmt.Errorf("h(%v): No such key %v", x, x)
+        }
+        fmt.Printf("h(%v): Refill map.\n", x)
+        fillMap2()
+        fmt.Printf("h(%v): Retry.\n", x)
+        return func() (Q, error) { return h2(x, k, true) }, nil
+    }
+    fmt.Printf("h(%v): Obtained %v. Continue to next step.\n", x, z)
+    var c Q
+    if k != nil {
+        c = k(queried)
+    }
+    return c, nil
+}
+
+func run2() {
+    var err error
+
+    fmt.Printf("========== Start2\n")
+
+    // Steps in reverse order. Execution order g1 -> g2 -> g3.
+    g3 := func (q bool) Q { return func () (Q, error) { return h2(3, nil, q) } }
+    g2 := func (q bool) Q { return func () (Q, error) { return h2(2, g3, q) } }
+    g1 := func () (Q, error) { return h2(1, g2, false) }
+    k := g1
+    for k != nil {
+        k, err = k()
+        if err != nil {
+            fmt.Printf("Received error: %v\n", err)
+            break
+        }
+    }
+    fmt.Printf("End2\n")
+}
+
+
+var m3 map[int]string
+
+func fillMap3() {
+    fmt.Printf("Filling map3\n")
+    if m3 == nil {
+        m3 = make(map[int]string)
+    }
+    m3[1] = "111"
+    m3[2] = "222"
+    m3[3] = "333"
+}
+
+// Add names to each continuation for pretty printing.
+type Q3 func() (Cont, error)
+
+type Cont struct {
+    name string
+    k Q3
+}
+
+func (c Cont) String() string {
+    return fmt.Sprintf("Cont{%v}", c.name)
+}
+
+func (c Cont) h3(x int, k func(bool) Cont, queried bool) (Cont, error) {
+    z, ok := m3[x]
+    if !ok {
+        fmt.Printf("[%v] h(%v): Missed key %v.\n", c, x, x)
+        if queried {
+            return Cont{k: nil}, fmt.Errorf("[%v] h(%v): No such key %v", c, x, x)
+        }
+        fmt.Printf("[%v] h(%v): Refill map.\n", c, x)
+        fillMap3()
+        fmt.Printf("[%v] h(%v): Retry.\n", c, x)
+        c.k = func() (Cont, error) { return c.h3(x, k, true) }
+        return c, nil
+    }
+    fmt.Printf("[%v] h(%v): Obtained %v.\n", c, x, z)
+    var next Cont
+    if k != nil {
+        next = k(queried)
+    }
+    fmt.Printf("[%v] h(%v): Continue to %v.\n", c, x, next)
+    return next, nil
+}
+
+func run3() {
+    var err error
+
+    fmt.Printf("========== Start3\n")
+
+    c3 := Cont{name: "c3"}
+    g3 := func (q bool) Cont {
+        c3.k = func () (Cont, error) { return c3.h3(3, nil, q) }
+        return c3
+    }
+
+    c2 := Cont{name: "c2"}
+    g2 := func (q bool) Cont {
+        c2.k = func () (Cont, error) { return c2.h3(2, g3, q) }
+        return c2
+    }
+
+    c1 := Cont{name: "c1"}
+    c1.k = func () (Cont, error) { return c1.h3(1, g2, false) }
+
+    c := c1
+    for c.k != nil {
+        fmt.Printf("Calling %v\n", c)
+        c, err = c.k()
+        if err != nil {
+            fmt.Printf("Received error: %v\n", err)
+            break
+        }
+    }
+    fmt.Printf("End3\n")
+}
+
+func main() {
+    run1()
+    run2()
+    run3()
+}
+