sort.Interface
Часть раздела Объектно-ориентированное программирование путешествия по GO на Coddy — урок 74 из 107.
Пакет sort предоставляет еще один отличный пример проектирования на основе интерфейсов. Чтобы отсортировать пользовательскую коллекцию, ваш тип должен реализовать sort.Interface:
type Interface interface {
Len() int
Less(i, j int) bool
Swap(i, j int)
}Эти три метода дают алгоритму сортировки всё необходимое: длину коллекции, способ сравнения элементов и способ их перестановки. Вот как сделать слайс пользовательских структур сортируемым:
type Person struct {
Name string
Age int
}
type ByAge []Person
func (a ByAge) Len() int { return len(a) }
func (a ByAge) Less(i, j int) bool { return a[i].Age < a[j].Age }
func (a ByAge) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
func main() {
people := []Person{
{"Alice", 30},
{"Bob", 25},
{"Carol", 35},
}
sort.Sort(ByAge(people))
fmt.Println(people)
// [{Bob 25} {Alice 30} {Carol 35}]
}Ключевая идея заключается в создании именованного типа (ByAge) на основе вашего среза. Это позволяет определять различное поведение сортировки для одних и тех же данных. Вы могли бы создать ByName с другой реализацией Less, чтобы вместо этого выполнять сортировку по алфавиту.
Как только ваш тип начнет удовлетворять sort.Interface, он автоматически заработает с sort.Sort(), sort.Reverse() и sort.IsSorted().
Задание
ЛегкоДавайте создадим систему инвентаризации товаров с возможностью сортировки, которая продемонстрирует мощь интерфейса sort.Interface в Go! Вы создадите тип Product и реализуете несколько стратегий сортировки, что позволит сортировать одну и ту же коллекцию разными способами.
Вы организуете свой код в двух файлах:
product.go: Определите тип продукта и реализации сортировки.Создайте структуру
Productс тремя полями:Name(string),Price(float64) иQuantity(int).Создайте два именованных типа на основе
[]Product:ByPrice— для сортировки товаров по цене в порядке возрастанияByQuantity— для сортировки товаров по количеству в порядке убывания (сначала наибольшее количество)
Каждый тип должен реализовать три метода, требуемых интерфейсом
sort.Interface:Len(),Less(i, j int)иSwap(i, j int). МетодLessопределяет порядок сортировки для каждого типа.main.go: Создайте и отсортируйте ваш инвентарь товаров.Считайте режим сортировки (
priceилиquantity), затем считайте количество товаров, а после — детали каждого товара. Каждый товар представлен тремя строками: название, цена и количество.Создайте срез (slice) товаров, отсортируйте их, используя соответствующий тип сортировки в зависимости от режима, а затем выведите каждый товар в следующем формате:
[Name]: $[Price] (x[Quantity])Выводите цены с двумя знаками после запятой.
Будут предоставлены следующие входные данные:
- Строка 1: Режим сортировки (
priceилиquantity) - Строка 2: Количество товаров
- Последующие строки: Детали товара (название, цена, количество — по три строки на каждый товар)
Например, для входных данных:
price
3
Laptop
999.99
5
Mouse
29.99
50
Keyboard
79.99
25Ваш вывод должен быть таким:
Mouse: $29.99 (x50)
Keyboard: $79.99 (x25)
Laptop: $999.99 (x5)И для входных данных:
quantity
3
Laptop
999.99
5
Mouse
29.99
50
Keyboard
79.99
25Ваш вывод должен быть таким:
Mouse: $29.99 (x50)
Keyboard: $79.99 (x25)
Laptop: $999.99 (x5)Обратите внимание, как одни и те же данные о товарах могут быть отсортированы по-разному простым использованием другого именованного типа. Как только ваши типы начинают удовлетворять sort.Interface, они бесшовно работают с функцией sort.Sort() из стандартной библиотеки.
Шпаргалка
Пакет sort требует, чтобы типы реализовывали интерфейс sort.Interface для обеспечения возможности сортировки:
type Interface interface {
Len() int
Less(i, j int) bool
Swap(i, j int)
}Чтобы сделать пользовательский срез сортируемым, создайте именованный тип и реализуйте три необходимых метода:
type Person struct {
Name string
Age int
}
type ByAge []Person
func (a ByAge) Len() int { return len(a) }
func (a ByAge) Less(i, j int) bool { return a[i].Age < a[j].Age }
func (a ByAge) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
// Sort the collection
sort.Sort(ByAge(people))Вы можете создать несколько именованных типов для одного и того же среза, чтобы реализовать различные варианты сортировки. Каждый тип определяет свой собственный метод Less для определения порядка сортировки.
Как только тип начинает удовлетворять интерфейсу sort.Interface, он может работать с функциями sort.Sort(), sort.Reverse() и sort.IsSorted().
Попробуйте сами
package main
import (
"bufio"
"fmt"
"os"
"sort"
"strconv"
)
func main() {
reader := bufio.NewReader(os.Stdin)
// Чтение режима сортировки
var mode string
fmt.Fscanln(reader, &mode)
// Чтение количества товаров
var count int
fmt.Fscanln(reader, &count)
// Чтение товаров
products := make([]Product, count)
for i := 0; i < count; i++ {
name, _ := reader.ReadString('\n')
name = name[:len(name)-1] // Удаление символа новой строки
priceStr, _ := reader.ReadString('\n')
priceStr = priceStr[:len(priceStr)-1]
price, _ := strconv.ParseFloat(priceStr, 64)
qtyStr, _ := reader.ReadString('\n')
qtyStr = qtyStr[:len(qtyStr)-1]
quantity, _ := strconv.Atoi(qtyStr)
products[i] = Product{Name: name, Price: price, Quantity: quantity}
}
// TODO: Отсортировать товары в зависимости от режима
// Если mode равен "price", использовать тип ByPrice
// Если mode равен "quantity", использовать тип ByQuantity
// Использовать sort.Sort() с соответствующим типом
// TODO: Вывести каждый товар в формате:
// [Name]: $[Price] (x[Quantity])
// Использовать fmt.Printf с %.2f для форматирования цены
}
В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.
Все уроки раздела Объектно-ориентированное программирование
1Основы ООП в Go
Внешние файлыРабочее пространство и модули GoПакеты и импортыЭкспортируемые и неэкспортируемые именаВведение в ООП в GoСтруктуры как классыОпределение методов структурПолучатели-указатели и получатели-значенияИнициализация структурФункции-конструкторыИтоги — Простой калькулятор4Интерфейсы
Введение в интерфейсыНеявная реализацияИнтерфейс как контрактПустой интерфейс (any)Утверждение типаПереключатель типовКомпозиция интерфейсовИнтерфейсы Stringer и ErrorПовторение: Калькулятор фигур7Инкапсуляция
Экспортируемые и неэкспортируемые поляИнкапсуляция на уровне пакетовГеттеры и сеттерыСокрытие информации в GoИтоги — Записи о студентах10Обобщения (Generics) (Go 1.18+)
Введение в GenericsПараметры типовОграничения типовОбобщенные структурыОбходной путь для обобщенных методовИтоги — Обобщенная коллекция13Паттерны проектирования. Часть 1
Введение в паттерны проектированияПаттерн SingletonПаттерн FactoryПаттерн Abstract FactoryПаттерн ObserverПаттерн Strategy2Глубокое погружение в типы и структуры
Базовые и составные типыОпределение пользовательских типовТеги структурАнонимные структурыВложенные структурыНулевые значения и значения по умолчаниюПовторение — Контактная книга5Композиция вместо наследования
Почему в Go нет наследованияОсновы встраивания структурПродвижение методовВстраивание нескольких структурВстраивание против агрегацииЗатенение встроенных методовИтоги — Иерархия сотрудников8Обработка ошибок и ООП
Интерфейс errorПользовательские типы ошибокОбертывание ошибок (fmt.Errorf)Sentinel-ошибкиerrors.Is() и errors.As()Panic, Defer и RecoverИтоги — Парсер файлов11Стандартная библиотека и ООП
io.Reader и io.Writersort.InterfaceИнтерфейс fmt.Stringerencoding/json со структурамиИнтерфейс http.HandlerПовторение: модели REST API14Паттерны проектирования. Часть 2
Паттерн КомандаПаттерн АдаптерПаттерн ДекораторПаттерн Шаблонный методПаттерн СостояниеПаттерн КомпоновщикMiddleware как Декоратор3Указатели и память
Основы указателей в GoУказатели на структурыПередача по значению и по ссылкеФункция new()Сборка мусора в GoПовторение: Конструктор связного списка6Полиморфизм в Go
Полиморфизм через интерфейсыУтиная типизация в GoПравила реализации интерфейсовПолиморфные коллекцииВнедрение зависимостейИтоги — Обработчик платежей9Конкурентность и ООП
Основы горутинКаналы и взаимодействиеБуферизованные и небуферизованные каналыОператор selectsync.Mutex и sync.RWMutexsync.WaitGroupПроектирование потокобезопасных структурПовторение — Worker Pool