Quellcode durchsuchen

Some cleanup, 100% coverage.

Peter H. Froehlich vor 12 Jahren
Ursprung
Commit
6c75ea15d3
3 geänderte Dateien mit 53 neuen und 17 gelöschten Zeilen
  1. 7 8
      README.md
  2. 28 9
      queue.go
  3. 18 0
      queue_test.go

+ 7 - 8
README.md

@@ -26,16 +26,15 @@ The benchmarks are not very sophisticated yet but it seems that we
 rather clearly beat container/list on the most common operations.
 
 ```
-$ go test -bench . -benchmem -cover
+$ go test -bench . -benchmem
 PASS
-BenchmarkPushFrontQueue	20000000	       191 ns/op	      53 B/op	       0 allocs/op
-BenchmarkPushFrontList	10000000	       290 ns/op	      49 B/op	       1 allocs/op
-BenchmarkPushBackQueue	10000000	       171 ns/op	      53 B/op	       0 allocs/op
+BenchmarkPushFrontQueue	20000000	       186 ns/op	      53 B/op	       0 allocs/op
+BenchmarkPushFrontList	 5000000	       302 ns/op	      49 B/op	       1 allocs/op
+BenchmarkPushBackQueue	20000000	       167 ns/op	      53 B/op	       0 allocs/op
 BenchmarkPushBackList	 5000000	       305 ns/op	      49 B/op	       1 allocs/op
-BenchmarkRandomQueue	 5000000	       418 ns/op	      26 B/op	       0 allocs/op
-BenchmarkRandomList	 2000000	       799 ns/op	      78 B/op	       1 allocs/op
-coverage: 84.1% of statements
-ok  	github.com/phf/go-queue	17.092s
+BenchmarkRandomQueue	 5000000	       422 ns/op	      26 B/op	       0 allocs/op
+BenchmarkRandomList	 2000000	       797 ns/op	      78 B/op	       1 allocs/op
+ok  	github.com/phf/go-queue	16.806s
 ```
 
 ## What I don't like about Go's conventions

+ 28 - 9
queue.go

@@ -29,13 +29,27 @@ func (q *Queue) Init() *Queue {
 	// start with a slice of length 2 even if that "wastes"
 	// some memory; we do front/back arithmetic modulo the
 	// length, so starting at 1 requires special cases
-	q.rep = make([]interface{}, 2) // TODO: keep old slice/array? but what if it's huge?
+	q.rep = make([]interface{}, 2)
+	// for some time I considered reusing the existing slice
+	// if all a client does is re-initialize the queue; the
+	// big problem with that is that the previous queue might
+	// have been huge while the current queue doesn't grow
+	// much at all; if that were to happen we'd hold on to a
+	// huge chunk of memory for just a few elements and nobody
+	// could do anything about it; so instead I decided to
+	// just allocate a new slice and let the GC take care of
+	// the previous one; seems a better tradeoff all around
 	q.front, q.back, q.length = 0, 0, 0
 	return q
 }
 
-// TODO: good idea? list.go does it but slows down every insertion...
-// I guess that's the price for allowing zero values to be useful?
+// lazyInit lazily initializes a zero Queue value.
+//
+// I am mostly doing this because container/list does the same thing.
+// Personally I think it's a little wasteful because every single
+// PushFront/PushBack is going to pay the overhead of calling this.
+// But that's the price for making zero values useful immediately,
+// something Go apparently likes a lot.
 func (q *Queue) lazyInit() {
 	if q.rep == nil {
 		q.Init()
@@ -47,14 +61,17 @@ func (q *Queue) Len() int {
 	return q.length
 }
 
+// empty returns true if the queue q has no elements.
 func (q *Queue) empty() bool {
 	return q.length == 0
 }
 
+// full returns true if the queue q is at capacity.
 func (q *Queue) full() bool {
 	return q.length == len(q.rep)
 }
 
+// grow doubles the size of queue q's underlying slice/array.
 func (q *Queue) grow() {
 	big := make([]interface{}, q.length*2)
 	j := q.front
@@ -68,12 +85,18 @@ func (q *Queue) grow() {
 }
 
 // TODO: leave this in or not?
+
 func (q *Queue) String() string {
-	result := fmt.Sprintf("(f: %d b: %d l:%d c:%d)", q.front, q.back, q.length, len(q.rep))
+//	result := fmt.Sprintf("(f: %d b: %d l:%d c:%d)", q.front, q.back, q.length, len(q.rep))
+	result := ""
 	result = result + "["
 	j := q.front
 	for i := 0; i < q.length; i++ {
-		result = result + fmt.Sprintf("[%v]", q.rep[j])
+		if i == q.length-1 {
+			result = result + fmt.Sprintf("%v", q.rep[j])
+		} else {
+			result = result + fmt.Sprintf("%v, ", q.rep[j])
+		}
 		q.inc(&j)
 	}
 	result = result + "]"
@@ -92,10 +115,6 @@ func (q *Queue) dec(i *int) {
 	*i = (*i-1+l) % l
 }
 
-// TODO: I dislike the Go philosophy of avoiding panics at all
-// costs; Front/Back/Pop from an empty Queue SHOULD panic! at
-// least in my mind...
-
 // Front returns the first element of queue q or nil. 
 func (q *Queue) Front() interface{} {
 	if q.empty() { return nil }

+ 18 - 0
queue_test.go

@@ -22,6 +22,12 @@ func ensureEmpty(t *testing.T, q *Queue) {
 	if e := q.Back(); e != nil {
 		t.Errorf("q.Back() = %v, want %v", e, nil)
 	}
+	if e := q.PopFront(); e != nil {
+		t.Errorf("q.PopFront() = %v, want %v", e, nil)
+	}
+	if e := q.PopBack(); e != nil {
+		t.Errorf("q.PopBack() = %v, want %v", e, nil)
+	}
 }
 
 func TestNew(t *testing.T) {
@@ -97,6 +103,18 @@ func TestZeroValue(t *testing.T) {
 	ensureLength(t, &q, 4)
 	q.PushFront(5)
 	ensureLength(t, &q, 5)
+	q.PushBack(6)
+	ensureLength(t, &q, 6)
+	q.PushBack(7)
+	ensureLength(t, &q, 7)
+	q.PushBack(8)
+	ensureLength(t, &q, 8)
+	q.PushBack(9)
+	ensureLength(t, &q, 9)
+	const want = "[5, 4, 3, 2, 1, 6, 7, 8, 9]"
+	if s := q.String(); s != want {
+		t.Errorf("q.String() = %s, want %s", s, want)
+	}
 }
 
 func BenchmarkPushFrontQueue(b *testing.B) {