// Fully persistent data structures. A persistent data structure is a data // structure that always preserves the previous version of itself when // it is modified. Such data structures are effectively immutable, // as their operations do not update the structure in-place, but instead // always yield a new structure. // // Persistent // data structures typically share structure among themselves. This allows // operations to avoid copying the entire data structure. package ps import ( "bytes" "fmt" ) // Any is a shorthand for Go's verbose interface{} type. type Any interface{} // A Map associates unique keys (type string) with values (type Any). type Map interface { // IsNil returns true if the Map is empty IsNil() bool // Set returns a new map in which key and value are associated. // If the key didn't exist before, it's created; otherwise, the // associated value is changed. // This operation is O(log N) in the number of keys. Set(key string, value Any) Map // Delete returns a new map with the association for key, if any, removed. // This operation is O(log N) in the number of keys. Delete(key string) Map // Lookup returns the value associated with a key, if any. If the key // exists, the second return value is true; otherwise, false. // This operation is O(log N) in the number of keys. Lookup(key string) (Any, bool) // Size returns the number of key value pairs in the map. // This takes O(1) time. Size() int // ForEach executes a callback on each key value pair in the map. ForEach(f func(key string, val Any)) // Keys returns a slice with all keys in this map. // This operation is O(N) in the number of keys. Keys() []string String() string } // Immutable (i.e. persistent) associative array const childCount = 8 const shiftSize = 3 type tree struct { count int hash uint64 // hash of the key (used for tree balancing) key string value Any children [childCount]*tree } var nilMap = &tree{} // Recursively set nilMap's subtrees to point at itself. // This eliminates all nil pointers in the map structure. // All map nodes are created by cloning this structure so // they avoid the problem too. func init() { for i := range nilMap.children { nilMap.children[i] = nilMap } } // NewMap allocates a new, persistent map from strings to values of // any type. // This is currently implemented as a path-copying binary tree. func NewMap() Map { return nilMap } func (self *tree) IsNil() bool { return self == nilMap } // clone returns an exact duplicate of a tree node func (self *tree) clone() *tree { var m tree m = *self return &m } // constants for FNV-1a hash algorithm const ( offset64 uint64 = 14695981039346656037 prime64 uint64 = 1099511628211 ) // hashKey returns a hash code for a given string func hashKey(key string) uint64 { hash := offset64 for _, codepoint := range key { hash ^= uint64(codepoint) hash *= prime64 } return hash } // Set returns a new map similar to this one but with key and value // associated. If the key didn't exist, it's created; otherwise, the // associated value is changed. func (self *tree) Set(key string, value Any) Map { hash := hashKey(key) return setLowLevel(self, hash, hash, key, value) } func setLowLevel(self *tree, partialHash, hash uint64, key string, value Any) *tree { if self.IsNil() { // an empty tree is easy m := self.clone() m.count = 1 m.hash = hash m.key = key m.value = value return m } if hash != self.hash { m := self.clone() i := partialHash % childCount m.children[i] = setLowLevel(self.children[i], partialHash>>shiftSize, hash, key, value) recalculateCount(m) return m } // replacing a key's previous value m := self.clone() m.value = value return m } // modifies a map by recalculating its key count based on the counts // of its subtrees func recalculateCount(m *tree) { count := 0 for _, t := range m.children { count += t.Size() } m.count = count + 1 // add one to count ourself } func (m *tree) Delete(key string) Map { hash := hashKey(key) newMap, _ := deleteLowLevel(m, hash, hash) return newMap } func deleteLowLevel(self *tree, partialHash, hash uint64) (*tree, bool) { // empty trees are easy if self.IsNil() { return self, false } if hash != self.hash { i := partialHash % childCount child, found := deleteLowLevel(self.children[i], partialHash>>shiftSize, hash) if !found { return self, false } newMap := self.clone() newMap.children[i] = child recalculateCount(newMap) return newMap, true // ? this wasn't in the original code } // we must delete our own node if self.isLeaf() { // we have no children return nilMap, true } /* if self.subtreeCount() == 1 { // only one subtree for _, t := range self.children { if t != nilMap { return t, true } } panic("Tree with 1 subtree actually had no subtrees") } */ // find a node to replace us i := -1 size := -1 for j, t := range self.children { if t.Size() > size { i = j size = t.Size() } } // make chosen leaf smaller replacement, child := self.children[i].deleteLeftmost() newMap := replacement.clone() for j := range self.children { if j == i { newMap.children[j] = child } else { newMap.children[j] = self.children[j] } } recalculateCount(newMap) return newMap, true } // delete the leftmost node in a tree returning the node that // was deleted and the tree left over after its deletion func (m *tree) deleteLeftmost() (*tree, *tree) { if m.isLeaf() { return m, nilMap } for i, t := range m.children { if t != nilMap { deleted, child := t.deleteLeftmost() newMap := m.clone() newMap.children[i] = child recalculateCount(newMap) return deleted, newMap } } panic("Tree isn't a leaf but also had no children. How does that happen?") } // isLeaf returns true if this is a leaf node func (m *tree) isLeaf() bool { return m.Size() == 1 } // returns the number of child subtrees we have func (m *tree) subtreeCount() int { count := 0 for _, t := range m.children { if t != nilMap { count++ } } return count } func (m *tree) Lookup(key string) (Any, bool) { hash := hashKey(key) return lookupLowLevel(m, hash, hash) } func lookupLowLevel(self *tree, partialHash, hash uint64) (Any, bool) { if self.IsNil() { // an empty tree is easy return nil, false } if hash != self.hash { i := partialHash % childCount return lookupLowLevel(self.children[i], partialHash>>shiftSize, hash) } // we found it return self.value, true } func (m *tree) Size() int { return m.count } func (m *tree) ForEach(f func(key string, val Any)) { if m.IsNil() { return } // ourself f(m.key, m.value) // children for _, t := range m.children { if t != nilMap { t.ForEach(f) } } } func (m *tree) Keys() []string { keys := make([]string, m.Size()) i := 0 m.ForEach(func(k string, v Any) { keys[i] = k i++ }) return keys } // make it easier to display maps for debugging func (m *tree) String() string { keys := m.Keys() buf := bytes.NewBufferString("{") for _, key := range keys { val, _ := m.Lookup(key) fmt.Fprintf(buf, "%s: %s, ", key, val) } fmt.Fprintf(buf, "}\n") return buf.String() }