Go Algorithms: Sorts

I started with Go's sort package for some inspiration on how to build the API for my implementations. It is nice to be able to look at other approaches, especially when I'm more accusotmed to doing things in Node.js or C#.

The Interface

Sorting algorithms generally only need to know how many elements, how to compare two elements, and a way to move elements to different positions. When using Go's sort package, you implement sort.Interface to provide those functions. I thought Sorter was a more helpful name for the interface, but it represents the same thing.

type (
	Sorter interface {
		Less(i, j int) bool
		Swap(i, j int)
		Len() int
	}
	SortAlgorithm func(s Sorter)
)

The SortAlgorithm describes the interface of an implementation. There isn't much of a point to being explicit about this other than decoupling the implementations from the testing code.

Implementations

You can find all of this information in the wikipedia entry. It's pretty brute force; just keep walking the collection and swapping values until it's sorted.

func BubbleSort(s Sorter) {
	length := s.Len()
	for sorted := false; !sorted; {
		sorted = true
		for i := 1; i < length; i++ {
			if s.Less(i, i-1) {
				s.Swap(i, i-1)
				sorted = false
			}
		}
	}
}

We can optimize it to make it a little faster by tryin to avoid iterating over parts of the collection that are already sorted:

func OptimizedBubbleSort(s Sorter) {
	length := s.Len()
	for length != 0 {
		newlength := 0
		for i := 1; i < length; i++ {
			if s.Less(i, i-1) {
				s.Swap(i, i-1)
				newlength = i
			}
		}
		length = newlength
	}
}

Taking it a bit further, we can try out a selection sort. Instead of thrashing back and forth with several swaps, we can swap when we find the smallest value.

func SelectionSort(s Sorter) {
	length := s.Len()
	for i := 0; i < length-1; i++ {
		min := i
		for j := i + 1; j < length; j++ {
			if s.Less(j, min) {
				min = j
			}
		}
		s.Swap(i, min)
	}
}

Example

We have a Person struct, and wish to sort a collection of Person objects.

type (
	Person struct {
		Age  int
		Name string
	}
	PeopleBy   func(p1, p2 *Person) bool
	sortPeople struct {
		people []Person
		by     PeopleBy
	}
)

The PeopleBy type represents a function that can compare two people. The naming might seem a bit curious, but I'm taking some creative license for the sake of experimenting with expressive code. More on that in a bit.

The sortPeople struct has the array of Person objects, and a PeopleBy function for dictating a sort order. The struct will implement the Sorter interface:

func (s *sortPeople) Len() int {
	return len(s.people)
}

func (s *sortPeople) Less(i, j int) bool {
	return s.by(&s.people[i], &s.people[j])
}

func (s *sortPeople) Swap(i, j int) {
	s.people[i], s.people[j] = s.people[j], s.people[i]
}

Here is an example of how this sorting code will ultimately be called:

func sortSomePeople(people) {
	ageAsc := func(p1, p2 *Person) bool {
		return p1.Age < p2.Age
	}
	PeopleBy(ageAsc).Sort(people)
}

The ageAsc function can be cast to PeopleBy, which has a method attached to it, Sort().

func (by PeopleBy) Sort(people []Person) {
	s := &sortPeople{people, by}
	BubbleSort(s)
}

Another way to do it would be to organize the code so that you could write something like this:

people.Sort(PeopleBy(ageAsc))

Conclusion

I've explored some rudimentary aspects of implementing sorting in Go. While it's a far cry from using something like LINQ in C#, learning to think about problems in the Go way is a way to gain a fresh perspective on the problem.

It took me a long time to get through this post. Several months, by my count! I'm still hoping to get back in the swing of blogging. Here's hoping that I will find time to get to more Algorithms in Go.

Show Comments