generic sorting

This commit is contained in:
Gregory 2021-12-16 17:46:10 +02:00
parent 323fd70370
commit c0dc901168
7 changed files with 41 additions and 66 deletions

View file

@ -2,15 +2,11 @@ package sorting
type insertion struct{} type insertion struct{}
func (*insertion) Sort(items Sortable) { func Insertion[T any](items []T, less func(a, b T) bool) {
len := items.Len() len := len(items)
for i := 1; i < len; i++ { for i := 1; i < len; i++ {
for j := i; j > 0 && items.Less(j, j-1); j-- { for j := i; j > 0 && less(items[j], items[j-1]); j-- {
items.Swap(j, j-1) items[j], items[j-1] = items[j-1], items[j]
} }
} }
} }
func NewInsertion() Sorter {
return &insertion{}
}

View file

@ -1,15 +0,0 @@
package sorting
type IntSort []int
func (items IntSort) Len() int {
return len(items)
}
func (items IntSort) Swap(i, j int) {
items[i], items[j] = items[j], items[i]
}
func (items IntSort) Less(i, j int) bool {
return items[i] < items[j]
}

View file

@ -1,20 +1,14 @@
package sorting package sorting
type selection struct{} func Selection[T any](items []T, less func(a, b T) bool) {
len := len(items)
func (*selection) Sort(items Sortable) {
len := items.Len()
for i := 0; i < len; i++ { for i := 0; i < len; i++ {
min := i min := i
for j := i + 1; j < len; j++ { for j := i + 1; j < len; j++ {
if items.Less(j, min) { if less(items[j], items[min]) {
min = j min = j
} }
} }
items.Swap(min, i) items[min], items[i] = items[i], items[min]
} }
} }
func NewSelection() Sorter {
return &selection{}
}

View file

@ -1,9 +1,7 @@
package sorting package sorting
type shell struct{} func Shell[T any](items []T, less func(a, b T) bool) {
len := len(items)
func (*shell) Sort(items Sortable) {
len := items.Len()
gap := 1 gap := 1
// Calculating gap maximum value. // Calculating gap maximum value.
@ -13,7 +11,7 @@ func (*shell) Sort(items Sortable) {
gap = gap*3 + 1 gap = gap*3 + 1
} }
// This loop needed to progressively degrease gap until siple insertion // This loop needed to progressively decrease gap until simple insertion
// sort will be used // sort will be used
for gap >= 1 { for gap >= 1 {
@ -22,8 +20,8 @@ func (*shell) Sort(items Sortable) {
// Instead of comparing adjacent elements we compare // Instead of comparing adjacent elements we compare
// gap distance elements and swap them // gap distance elements and swap them
for j := i; j >= gap && items.Less(j, j-gap); j -= gap { for j := i; j >= gap && less(items[j], items[j-gap]); j -= gap {
items.Swap(j, j-gap) items[j], items[j-gap] = items[j-gap], items[j]
} }
} }
@ -31,7 +29,3 @@ func (*shell) Sort(items Sortable) {
gap = gap / 3 gap = gap / 3
} }
} }
func NewShell() Sorter {
return &shell{}
}

View file

@ -1,13 +1,3 @@
package sorting package sorting
type Sortable interface { type SliceSorter[T any] func([]T, func(T, T) bool)
Len() int
Swap(i, j int)
Less(i, j int) bool
}
type Sorter interface {
Sort(Sortable)
// TODO: add generic slice sort when type variables are landed
// SortSlice[T any](T, func(i, j int) bool)
}

View file

@ -4,32 +4,32 @@ import (
"testing" "testing"
) )
func TestSelection(t *testing.T) { func TestSelectionSlice(t *testing.T) {
CheckSorter(NewSelection()) CheckSliceSorter(Selection[int])
} }
func BenchmarkSelection(b *testing.B) { func BenchmarkSelection(b *testing.B) {
for i := 0; i < b.N; i++ { for i := 0; i < b.N; i++ {
BenchmarkSort(10000, NewSelection()) BenchmarkSort(10000, Selection[int])
} }
} }
func TestInsertion(t *testing.T) { func TestInsertion(t *testing.T) {
CheckSorter(NewInsertion()) CheckSliceSorter(Insertion[int])
} }
func BenchmarkInsertion(b *testing.B) { func BenchmarkInsertion(b *testing.B) {
for i := 0; i < b.N; i++ { for i := 0; i < b.N; i++ {
BenchmarkSort(10000, NewInsertion()) BenchmarkSort(10000, Insertion[int])
} }
} }
func TestShell(t *testing.T) { func TestShell(t *testing.T) {
CheckSorter(NewShell()) CheckSliceSorter(Shell[int])
} }
func BenchmarkShell(b *testing.B) { func BenchmarkShell(b *testing.B) {
for i := 0; i < b.N; i++ { for i := 0; i < b.N; i++ {
BenchmarkSort(10000, NewShell()) BenchmarkSort(10000, Shell[int])
} }
} }

View file

@ -7,6 +7,20 @@ import (
"time" "time"
) )
type IntSort []int
func (items IntSort) Len() int {
return len(items)
}
func (items IntSort) Swap(i, j int) {
items[i], items[j] = items[j], items[i]
}
func (items IntSort) Less(i, j int) bool {
return items[i] < items[j]
}
func SameInts(a, b []int) bool { func SameInts(a, b []int) bool {
if len(a) != len(b) { if len(a) != len(b) {
return false return false
@ -21,14 +35,16 @@ func SameInts(a, b []int) bool {
return true return true
} }
func CheckSorter(s Sorter) { func intCmp(a, b int) bool { return a < b }
func CheckSliceSorter(sorter SliceSorter[int]) {
rand.Seed(time.Now().Unix()) rand.Seed(time.Now().Unix())
actual := rand.Perm(1000) actual := rand.Perm(1000)
expected := make([]int, len(actual)) expected := make([]int, len(actual))
copy(expected, actual) copy(expected, actual)
s.Sort(IntSort(actual)) sorter(actual, intCmp)
sort.Sort(IntSort(expected)) sort.Sort(IntSort(expected))
if !SameInts(actual, expected) { if !SameInts(actual, expected) {
@ -36,8 +52,8 @@ func CheckSorter(s Sorter) {
} }
} }
func BenchmarkSort(numItems int, s Sorter) { func BenchmarkSort(numItems int, sorter SliceSorter[int]) {
rand.Seed(time.Now().Unix()) rand.Seed(time.Now().Unix())
items := rand.Perm(numItems) items := rand.Perm(numItems)
s.Sort(IntSort(items)) sorter(items, intCmp)
} }