type Tree struct {cmp func(interface{}, interface{}) introot *Node}type Node struct {val interface{}left, right *Tree}
Remove isn't really that bad - if you use sort.Interface (or something similar with methods relevant here), you can do a compacting remove with two operations: s.Swap(i, s.Len()-1), s.RemoveLastElement().
--
You received this message because you are subscribed to the Google Groups "golang-nuts" group.
To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts...@googlegroups.com.
For more options, visit https://groups.google.com/groups/opt_out.
quick check: Assign a new handle once per 10 nanoseconds -> running
out of them in 2^63 * 10^-8s, which is about 2923 years B-)
Note that there's not actually two copies of the data. The tree only holds the index. Think of the index as a pointer to the actual value.Jan: about using a map - I thought of that too, but you still need to compact the keyspace... i.e. if you remove index "7" from the map, you need to be able to figure out that "7" is a usable key again. I guess if you use a uint64 as your key, you have a long time before you run out of keys just by incrementing the last used index on insert.
Can't you use John's suggestion of swapping the last index into the "empty spot" now made free by the removal of 7? The only difficulty is that you have to find the node corresponding to the element moved into the 7 slot. This can be done with the auxiliary slice mapping indices to tree node pointers (a slight improvement on my suggestion above):
Give a reason me a reason not using it.
- make access slower (even if a little).
- requires type assertions to use.
I think the interface{} here is used as a safe pointer. Accessing the value is only a de-refference, while reading from an array is almost the same. For type checking, if the type to unbox is the same as the one in it, the checking can be optimized by the compiler to very efficient. maybe just an int comparing. This is compared to the proposed storing data in an array, in which case, deleting is not that efficient.
package mainimport ("testing")// StringData is just a slice that implements a comparison functiontype StringData []string// Cmp returns a closure that will compare the given string with// the string located at an index of the underlying slicefunc (s StringData) Cmp(val string) func(int) int8 {// this is just a standard string compare based on the runesreturn func(idx int) int8 {other := []rune(s[idx])for i, r := range val {if i > len(other) {return 1}c := r - other[i]if c < 0 {return -1}if c > 0 {return 1}}return 0}}type Tree struct {Val interface{}Left *TreeRight *Tree}// New Returns a newly initialized Tree.func New() *Tree {return &Tree{Val: nil}}// Insert adds the value to the tree.//// i is the key of the item in your datastructure//// cmp is a comparison function that should return// -1 if less than data[i], 0 if equal, and 1 if greater thanfunc (t *Tree) Insert(v interface{}, cmp func(interface{}, interface{}) int8) {if t.Val == nil {t.Val = vreturn}for {switch cmp(t.Val, v) {case -1:if t.Left == nil {t.Left = &Tree{Val: v}return}t = t.Leftcase 0, 1:if t.Right == nil {t.Right = &Tree{Val: v}return}t = t.Rightdefault:panic("Comparison function should only return 0, 1, or -1")}}panic("impossible")}// Find returns the index of the item if it is in the tree or -1 if it is not.//// cmp is a comparison function that should return// -1 if less than data[i], 0 if equal, and 1 if greater thanfunc (t *Tree) Search(v interface{}, cmp func(interface{}, interface{}) int8) interface{} {t = t.search(v, cmp)if t == nil {return -1}return t.Val}func (t *Tree) search(v interface{}, cmp func(interface{}, interface{}) int8) *Tree {if cmp == nil {panic("Nil comparison function!")}for t != nil {switch cmp(t.Val, v) {case -1:t = t.Leftcase 0:return tcase 1:t = t.Rightdefault:panic("Comparison function should only return 0, 1, or -1")}}return nil}func Cmp(a, b interface{}) int8 {other := []rune(b.(string))for i, r := range a.(string) {if i > len(other) {return 1}c := r - other[i]if c < 0 {return -1}if c > 0 {return 1}}return 0}func BenchmarkSlice(b *testing.B) {s := StringData([]string{"banana", "apple", "domino", "cat", "zebra", "monkey", "hippo"})
for i := 0; i < b.N; i++ {
t := tree.New()for i, v := range s {t.Insert(i, s.Cmp(v))}t.Search(s.Cmp("domino"))}}func BenchmarkInterface(b *testing.B) {s := StringData([]string{"banana", "apple", "domino", "cat", "zebra", "monkey", "hippo"})
for i := 0; i < b.N; i++ {
t := New()for _, v := range s {t.Insert(v, Cmp)}t.Search("domino", Cmp)}}
BenchmarkSlice 200000 10505 ns/opBenchmarkInterface 500000 6592 ns/op
LEAL l+-44(SP),BX
CMPL 4(BX),$0
JHI ,16
CALL ,runtime.panicindex+0(SB)
MOVL (BX),BX
MOVL (BX),BP
MOVL $type.int+0(SB),(SP)
LEAL s+-28(SP),SI
LEAL 4(SP),DI
CLD ,
MOVSL ,
MOVSL ,
CALL ,runtime.assertE2T+0(SB)
While in a binary tree algorithm, the data you are going to process are piles of elements separately. They have to be inserted into the tree one-by-one. Since you'd like to use the tree, you have to use the tree data-structure here(you data is embedded in), using interface{} or not.
This is actually the first time a had a look at Nate's code and the
closure approach is definitely not the optimal one. And that's exactly
what your benchmark proves.
I would suggest to shape the comparator function as: `cmp func(a, b
int) int`, where a and b are indexes/handles which contents should be
compared. At insert, the item get's an index/slot _before_ that index
being put in a new tree node at the proper place of the tree. For
search I would probably "waste" a well known index like, say 0.
Then I'd expect the []int, []string, ... to beat the []interface{} at any time.
--
You received this message because you are subscribed to the Google Groups "golang-nuts" group.
To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts...@googlegroups.com.
For more options, visit https://groups.google.com/groups/opt_out.
I guess we could throw this in with the int64 optimization and just throw away indexes when we search (there's opportunity to recover the wasted key if we finish the search and see that no one has "used up" the next-highest index).
func BenchmarkInterfaceInsert(b *testing.B) {
for i := 0; i < b.N; i++ {
b.StopTimer()t := makeTree()b.StartTimer()t.Insert("pineapple")t.Insert("grape")t.Insert("cherry")}}func BenchmarkInterfaceFind(b *testing.B) {b.StopTimer()t := makeTree()
b.StartTimer()for i := 0; i < b.N; i++ {
t.Find("monkey")t.Find("hippo")}}
This makes the timing more accurate thus more stable.
David
BenchmarkInsert 500000 3270 ns/opBenchmarkFind 1000000 1639 ns/opBenchmarkInterfaceInsert 500000 4175 ns/opBenchmarkInterfaceFind 1000000 2156 ns/opBenchmarkUnsafeInsert 1000000 2609 ns/opBenchmarkUnsafeFind 1000000 1609 ns/op