RosettaCodeData/Task/History-variables/Go/history-variables.go
Ingy döt Net db842d013d A-M baby
2013-04-10 21:29:02 -07:00

113 lines
2.8 KiB
Go

package main
import (
"fmt"
"sort"
"sync"
"time"
)
// data type for history variable (its an int)
type history struct {
timestamp tsFunc
hs []hset
}
// data type for timestamp generator
type tsFunc func() time.Time
// data type for a "set" event
type hset struct {
int // new value
t time.Time // timestamp
}
// newHistory creates a history variable
func newHistory(ts tsFunc) history {
return history{ts, []hset{{t: ts()}}}
}
// int returns the current value
func (h history) int() int {
return h.hs[len(h.hs)-1].int
}
// set does what you expect and returns the timestamp recorded for the event
func (h *history) set(x int) time.Time {
t := h.timestamp()
h.hs = append(h.hs, hset{x, t})
return t
}
// dump displays a complete history
func (h history) dump() {
for _, hs := range h.hs {
fmt.Println(hs.t.Format(time.StampNano), hs.int)
}
}
// recall recalls the value stored in the history variable at time t.
// if the variable had not been created yet, ok is false.
func (h history) recall(t time.Time) (int, /*ok*/ bool) {
i := sort.Search(len(h.hs), func(i int) bool {
return h.hs[i].t.After(t)
})
if i > 0 {
return h.hs[i-1].int, true
}
return 0, false
}
// newTimestamper returns a function that generates unique timestamps.
// Use a single timestamper for multiple history variables to preserve
// an unambiguous sequence of assignments across the multiple history
// variables within a single goroutine.
func newTimestamper() tsFunc {
var last time.Time
return func() time.Time {
if t := time.Now(); t.After(last) {
last = t
} else {
last.Add(1)
}
return last
}
}
// newProtectedTimestamper generates unique timestamps for concurrent
// goroutines.
func newProtectedTimestamper() tsFunc {
var last time.Time
var m sync.Mutex
return func() (t time.Time) {
t = time.Now()
m.Lock() // m protects last
if t.After(last) {
last = t
} else {
last.Add(1)
t = last
}
m.Unlock()
return
}
}
func main() {
// enable history variable support appropriate for single goroutine.
ts := newTimestamper()
// define a history variable
h := newHistory(ts)
// assign three values. (timestamps kept for future reference.)
ref := []time.Time{h.set(3), h.set(1), h.set(4)}
// non-destructively display history
fmt.Println("History of variable h:")
h.dump()
// recall the three values. (this is non-destructive as well, but
// different than the dump in that values are recalled by time.)
fmt.Println("Recalling values:")
for _, t := range ref {
rv, _ := h.recall(t)
fmt.Println(rv)
}
}