Chapter 7 interface
Interface types are abstractions and generalizations of other types of behavior Interface types will not be bound with specific implementation details. This abstract way can make our functions more flexible and adaptable
The interface of Go language is special because it meets the implicit implementation. In other words, we do not need to define all interface types that meet the requirements for a specific type, but just let the type have some simple and necessary methods. In this way, we create a new interface type to meet the specific types, and we do not need to change the definitions of these types. This mechanism is useful when the types we use come from packages that are not under our control
7.6 sort.Interface interface
Sorting and string formatting are operations we often use
The sort package in Go language has built-in functions that can sort any sequence. It is a little special because sort The sort function does not make any assumptions about any specific sequence or its elements. It uses an interface type sort Interface to specify the Convention between the general sorting algorithm and the sequence types that may need to participate in sorting
The implementation of the above interface is determined by the specific representation of the sequence and the elements it wants to sort. The representation of the sequence is usually a slice
A built-in sorting algorithm needs to know three elements: sequence length, comparison results of two elements, and exchange mode of two (elements)
package sort type Interface interface { Len() int Less(i,j int)bool //i,j are indices of sequence elements Swap(i,j int) }
In order to sort the sequence, we also have to define the specific types that implement the above methods. Let's take the string slice as an example
type StringSlice []string func (p StringSlice) Len() int {return len(p)} func (p StringSlice) Less(i,j int)bool {return p[i]<p[j]} func (p StringSlice) Swap(i,j int) {p[i],p[j]= p[j],p[i]}
Now we convert a slice to a StringSlice type for sorting:
func Sort(data Interface) { n := data.Len() quickSort(data, 0, n, maxDepth(n)) } sort.Sort(StringSlice(names))
OK, now that the sorting code has been built, let's actually use it. Let's sort the music playlists in a table. Each element is not itself but a pointer, which will be faster
type Track struct { Title string Artist string Album string Year int Length time.Duration } var tracks = []*Track{ {"Go", "Delilah", "From the Roots Up", 2012, length("3m38s")}, {"Go", "Moby", "Moby", 1992, length("3m37s")}, {"Go Ahead", "Alicia Keys", "As I Am", 2007, length("4m36s")}, {"Read 2 Go", "Martin Solveig", "Smash", 2011, length("4m24s")}, } func length(s string) time.Duration { d, err := time.ParseDuration(s) if err != nil { panic(s) } return d }
We use the printTracks function to print the playlist into a table. A graphical presentation may be better. We use the text/tabwriter package to generate a table with neatly aligned and separated columns, as follows:
func printTracks(tracks []*Track) { const format = "%v\t%v\t%v\t%v\t%v\t%v\t\n" tw := new(tabwriter.Writer).Init(os.Stdout, 0, 8, 2, ' ', 0) fmt.Fprintf(tw, format, "Title", "Artist", "Album", "Year", "Length") fmt.Fprintf(tw, format, "-----", "-----", "-----", "-----", "----", "------") for _, t := range tracks { fmt.Fprintf(tw, format, t.Title, t.Artist, t.Album, t.Year, t.Length) } tw.Flush() //calculate column widths and print table }
Note: * tabwriter Writer is to meet io The writer interface collects every piece of data written to it: its flush method formats the entire table and writes it to OS Stdout (standard output)
In order to sort the playlist according to the Artist field, we will define a new slice type with the required Len, Less and Swap methods like StringSlice
type byArtist []*Track func (x byArtist) Len() int { return len(x) } func (x byArtist) Less(i, j int) bool { return x[i].Artist < x[j].Artist } func (x byArtist) Swap(i, j int) { x[i], x[j] = x[j], x[i] }
In order to call the sorter, we must first convert Tracks to the new byArtist type, which defines the specific sort, and we can continue to call the sort function
func main() { sort.Sort(byArtist(tracks)) printTracks(tracks) sort.Sort(sort.Reverse(byArtist(tracks))) printTracks(tracks) }
Title Artist Album Year Length ----- ----- ----- ---- ------ Go Ahead Alicia Keys As I Am 2007 4m36s Go Delilah From the Roots Up 2012 3m38s Read 2 Go Martin Solveig Smash 2011 4m24s Go Moby Moby 1992 3m37s Title Artist Album Year Length ----- ----- ----- ---- ------ Go Moby Moby 1992 3m37s Read 2 Go Martin Solveig Smash 2011 4m24s Go Delilah From the Roots Up 2012 3m38s Go Ahead Alicia Keys As I Am 2007 4m36s
Sort. Is used on it Reverse function, let's take a closer look
func Reverse(data Interface) Interface { return &reverse{data} } func (r reverse) Less(i, j int) bool { return r.Interface.Less(j, i) } type reverse struct { // This embedded Interface permits Reverse to use the methods of // another Interface implementation. Interface }
The other two methods of reverse, Len and Swap, are implicitly composed of the original embedded sort Interface is provided. Because reverse is an undisclosed type, the export function reverse returns a message containing the original sort Reverse instance of interface value
Let's redefine a new type byYear
type byYear []*Track func (x byYear) Len() int { return len(x) } func (x byYear) Less(i, j int) bool { return x[i].Year < x[j].Year } func (x byYear) Swap(i, j int) { x[i], x[j] = x[j], x[i] }
func main() { sort.Sort(byYear(tracks)) printTracks(tracks) }
Print out the results. It's perfect
Title Artist Album Year Length ----- ----- ----- ---- ------ Go Moby Moby 1992 3m37s Go Ahead Alicia Keys As I Am 2007 4m36s Read 2 Go Martin Solveig Smash 2011 4m24s Go Delilah From the Roots Up 2012 3m38s
Let's continue to look at a nested sort (multi condition) and print the results
type customSort struct { t []*Track less func(x, y *Track) bool } func (x customSort) Len() int { return len(x.t) } func (x customSort) Less(i, j int) bool { return x.less(x.t[i], x.t[j]) } func (x customSort) Swap(i, j int) { x.t[i], x.t[j] = x.t[j], x.t[i] } func main() { sort.Sort(customSort{tracks, func(x, y *Track) bool { if x.Title != y.Title { return x.Title < y.Title } if x.Year != y.Year { return x.Year < y.Year } if x.Length != y.Length { return x.Length < y.Length } return false }}) printTracks(tracks) }
Title Artist Album Year Length ----- ----- ----- ---- ------ Go Moby Moby 1992 3m37s Go Delilah From the Roots Up 2012 3m38s Go Ahead Alicia Keys As I Am 2007 4m36s Read 2 Go Martin Solveig Smash 2011 4m24s
As we can see, to achieve this sort, That means that the time complexity is O (n logn). Checking whether a sequence is ordered requires at least n-1 comparisons. The IsSort function in the sort package helps us do this check. Like sort.Sort, it also abstracts the sequence and its sorting function using sort.Interface, but it never calls the Swap method: the following is a code example:
values := []int{3, 1, 4, 1} fmt.Println(sort.IntsAreSorted(values)) sort.Ints(values) fmt.Println(values) fmt.Println(sort.IntsAreSorted(values)) sort.Sort(sort.Reverse(sort.IntSlice(values))) fmt.Println(values) fmt.Println(sort.IntsAreSorted(values))
false [1 1 3 4] true [4 3 1 1] false
For ease of use, the sort package provides specific versions and function types for the normal sorting of [] int \ []string[]float64. For other types, such as [] int64 \ or [] uint, although the path is very simple, we need to implement it manually