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() из стандартной библиотеки.
Попробуйте сами
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Потренируйтесь самостоятельно: Онлайн-компилятор Go