From bb5ef95c07d802787b7e9461391d2c65690b2394 Mon Sep 17 00:00:00 2001 From: Dusan Brajovic Date: Wed, 3 Jul 2024 02:34:31 +0200 Subject: [PATCH 1/2] use generic queue --- benchmark_test.go | 14 +++++++------- examples_test.go | 20 ++++++++++---------- mock_test.go | 11 +++-------- queue.go | 22 +++++++++++----------- queue_test.go | 36 ++++++++++++------------------------ types.go | 4 ++-- 6 files changed, 45 insertions(+), 62 deletions(-) diff --git a/benchmark_test.go b/benchmark_test.go index 1b3c2e3..1f73194 100644 --- a/benchmark_test.go +++ b/benchmark_test.go @@ -8,7 +8,7 @@ func benchmarkPush(b *testing.B, items []*mockItem) { b.Helper() for i := 0; i < b.N; i++ { - q := NewQueue() + q := NewQueue[*mockItem]() for _, item := range items { q.Push(item) } @@ -32,12 +32,12 @@ func BenchmarkHeap_Push100(b *testing.B) { func benchmarkPop( b *testing.B, items []*mockItem, - popCallback func(q *Queue), + popCallback func(q *Queue[*mockItem]), ) { b.Helper() for i := 0; i < b.N; i++ { - q := NewQueue() + q := NewQueue[*mockItem]() b.StopTimer() @@ -57,7 +57,7 @@ func BenchmarkHeap_PopFront10(b *testing.B) { items := generateRandomItems(10) b.ResetTimer() - benchmarkPop(b, items, func(q *Queue) { + benchmarkPop(b, items, func(q *Queue[*mockItem]) { q.PopFront() }) } @@ -66,7 +66,7 @@ func BenchmarkHeap_PopFront100(b *testing.B) { items := generateRandomItems(100) b.ResetTimer() - benchmarkPop(b, items, func(q *Queue) { + benchmarkPop(b, items, func(q *Queue[*mockItem]) { q.PopFront() }) } @@ -75,7 +75,7 @@ func BenchmarkHeap_PopBack10(b *testing.B) { items := generateRandomItems(10) b.ResetTimer() - benchmarkPop(b, items, func(q *Queue) { + benchmarkPop(b, items, func(q *Queue[*mockItem]) { q.PopBack() }) } @@ -84,7 +84,7 @@ func BenchmarkHeap_PopBack100(b *testing.B) { items := generateRandomItems(100) b.ResetTimer() - benchmarkPop(b, items, func(q *Queue) { + benchmarkPop(b, items, func(q *Queue[*mockItem]) { q.PopBack() }) } diff --git a/examples_test.go b/examples_test.go index d913f2b..d8d48f5 100644 --- a/examples_test.go +++ b/examples_test.go @@ -6,47 +6,47 @@ type number struct { value int } -func (i number) Less(other Item) bool { - return i.value < other.(number).value +func (i number) Less(other number) bool { + return i.value < other.value } func ExampleQueue_Push() { - q := NewQueue() + q := NewQueue[number]() q.Push(number{1}) // 1 q.Push(number{2}) // 2 q.Push(number{3}) // 3 - fmt.Println(q[0].(number).value) + fmt.Println(q[0].value) // Output: 1 } func ExampleQueue_PopFront() { - q := NewQueue() + q := NewQueue[number]() q.Push(number{1}) // 1 q.Push(number{2}) // 2 q.Push(number{3}) // 3 popped := q.PopFront() - fmt.Println(popped.(number).value) + fmt.Println(popped.value) // Output: 1 } func ExampleQueue_PopBack() { - q := NewQueue() + q := NewQueue[number]() q.Push(number{1}) // 1 q.Push(number{2}) // 2 q.Push(number{3}) // 3 popped := q.PopBack() - fmt.Println(popped.(number).value) + fmt.Println(popped.value) // Output: 3 } func ExampleQueue_Fix() { - q := NewQueue() + q := NewQueue[number]() q.Push(number{1}) // 1 q.Push(number{2}) // 2 @@ -55,6 +55,6 @@ func ExampleQueue_Fix() { q[0] = number{4} // 1 -> 4 q.Fix() - fmt.Println(q[0].(number).value) + fmt.Println(q[0].value) // Output: 2 } diff --git a/mock_test.go b/mock_test.go index 2fecb7f..cce8134 100644 --- a/mock_test.go +++ b/mock_test.go @@ -2,7 +2,7 @@ package queue import "fmt" -type lessDelegate func(Item) bool +type lessDelegate func(item *mockItem) bool // mockItem is a mockable Item type mockItem struct { @@ -11,17 +11,12 @@ type mockItem struct { value int } -func (m *mockItem) Less(i Item) bool { +func (m *mockItem) Less(i *mockItem) bool { if m.lessFn != nil { return m.lessFn(i) } - other, ok := i.(*mockItem) - if !ok { - return false - } - - return m.value < other.value + return m.value <= i.value } func (m *mockItem) String() string { diff --git a/queue.go b/queue.go index c5670f0..3bff208 100644 --- a/queue.go +++ b/queue.go @@ -1,26 +1,26 @@ package queue // Queue is the priority queue based on insertion sort -type Queue []Item +type Queue[T Item[T]] []T // NewQueue creates an instance of the priority queue -func NewQueue() Queue { - return make(Queue, 0) +func NewQueue[T Item[T]]() Queue[T] { + return make(Queue[T], 0) } // Len returns the length of the queue -func (q *Queue) Len() int { +func (q *Queue[T]) Len() int { return len(*q) } // Index returns the element at the specified index, if any. // NOTE: panics if out of bounds -func (q *Queue) Index(index int) Item { +func (q *Queue[T]) Index(index int) T { return (*q)[index] } // Push adds a new element to the priority queue -func (q *Queue) Push(item Item) { +func (q *Queue[T]) Push(item T) { *q = append(*q, item) for i := len(*q) - 1; i > 0; i-- { if (*q)[i].Less((*q)[i-1]) { @@ -33,7 +33,7 @@ func (q *Queue) Push(item Item) { } // Fix makes sure the priority queue is properly sorted -func (q *Queue) Fix() { +func (q *Queue[T]) Fix() { for i := 1; i < len(*q); i++ { for j := i - 1; j >= 0; j-- { if (*q)[j].Less((*q)[j+1]) { @@ -46,7 +46,7 @@ func (q *Queue) Fix() { } // PopFront removes the first element in the queue, if any -func (q *Queue) PopFront() Item { +func (q *Queue[T]) PopFront() *T { if len(*q) == 0 { return nil } @@ -54,11 +54,11 @@ func (q *Queue) PopFront() Item { el := (*q)[0] *q = (*q)[1:] - return el + return &el } // PopBack removes the last element in the queue, if any -func (q *Queue) PopBack() Item { +func (q *Queue[T]) PopBack() *T { if len(*q) == 0 { return nil } @@ -66,5 +66,5 @@ func (q *Queue) PopBack() Item { el := (*q)[len(*q)-1] *q = (*q)[:len(*q)-1] - return el + return &el } diff --git a/queue_test.go b/queue_test.go index 111b382..dc6137d 100644 --- a/queue_test.go +++ b/queue_test.go @@ -10,19 +10,9 @@ import ( ) // isSorted checks if the queue is sorted -func isSorted(q Queue, ascending bool) bool { - cmp := func(a, b *mockItem) bool { - return a.value <= b.value - } - - if !ascending { - cmp = func(a, b *mockItem) bool { - return a.value >= b.value - } - } - +func isSorted[T Item[T]](q Queue[T]) bool { for i := 0; i < len(q)-1; i++ { - if !cmp(q[i].(*mockItem), q[i+1].(*mockItem)) { + if !q[i].Less(q[i+1]) { return false } } @@ -76,7 +66,7 @@ func TestQueue_Insert(t *testing.T) { ) // Create a new queue - q := NewQueue() + q := NewQueue[*mockItem]() // Push items for _, item := range items { @@ -84,10 +74,8 @@ func TestQueue_Insert(t *testing.T) { if !testCase.ascending { // Prep the items if needed (min / max) - item.lessFn = func(i Item) bool { - other, _ := i.(*mockItem) - - return item.value >= other.value + item.lessFn = func(i *mockItem) bool { + return item.value >= i.value } } @@ -95,7 +83,7 @@ func TestQueue_Insert(t *testing.T) { } assert.Len(t, q, numItems) - assert.True(t, isSorted(q, testCase.ascending)) + assert.True(t, isSorted(q)) }) } } @@ -109,7 +97,7 @@ func TestQueue_PopFront(t *testing.T) { ) // Create a new queue - q := NewQueue() + q := NewQueue[*mockItem]() // Push items for _, item := range items { @@ -127,7 +115,7 @@ func TestQueue_PopFront(t *testing.T) { for _, item := range items { popped := q.PopFront() - assert.Equal(t, item, popped) + assert.Equal(t, item, *popped) } assert.Len(t, q, 0) @@ -143,7 +131,7 @@ func TestQueue_PopBack(t *testing.T) { ) // Create a new queue - q := NewQueue() + q := NewQueue[*mockItem]() // Push items for _, item := range items { @@ -161,7 +149,7 @@ func TestQueue_PopBack(t *testing.T) { for i := len(items) - 1; i >= 0; i-- { popped := q.PopBack() - assert.Equal(t, items[i], popped) + assert.Equal(t, items[i], *popped) } assert.Len(t, q, 0) @@ -177,7 +165,7 @@ func TestQueue_Fix(t *testing.T) { ) // Create a new queue - q := NewQueue() + q := NewQueue[*mockItem]() // Push items for _, item := range items { @@ -197,6 +185,6 @@ func TestQueue_Fix(t *testing.T) { q.Fix() assert.Equal(t, q.Len(), numItems) - assert.True(t, isSorted(q, true)) + assert.True(t, isSorted(q)) assert.Equal(t, newItem, q.Index(0)) } diff --git a/types.go b/types.go index c770847..1536a6d 100644 --- a/types.go +++ b/types.go @@ -1,9 +1,9 @@ package queue // Item represents a single queue item -type Item interface { +type Item[T any] interface { // Less returns a flag indicating if the element // has a lower value than another element. // For max-priority queue implementations, Less should return true if A > B - Less(b Item) bool + Less(b T) bool } From 836bb8b6117b733de02cd6de9fd2793a71a55b9b Mon Sep 17 00:00:00 2001 From: Dusan Brajovic Date: Sun, 7 Jul 2024 18:47:54 +0200 Subject: [PATCH 2/2] Drop unused linter --- .golangci.yaml | 1 - 1 file changed, 1 deletion(-) diff --git a/.golangci.yaml b/.golangci.yaml index 9a515f0..ec7ea1e 100644 --- a/.golangci.yaml +++ b/.golangci.yaml @@ -40,7 +40,6 @@ linters: - gosimple # Code simplification - govet # Official Go tool - ineffassign # Detects when assignments to existing variables are not used - - interfacer # Top-level interface suggestions - nakedret # Finds naked/bare returns and requires change them - nilerr # Requires explicit returns - nilnil # Requires explicit returns