sort.Interface
Часть раздела Объектно-ориентированное программирование путешествия по GO на Coddy. Урок 74 из 107.
Пакет sort представляет собой ещё один отличный пример дизайна на основе интерфейсов. Чтобы отсортировать пользовательскую коллекцию, ваш тип должен реализовывать sort.Interface:
type Interface interface {
Len() int
Less(i, j int) bool
Swap(i, j int)
}Эти три метода предоставляют алгоритму сортировки всё необходимое: length коллекции, способ сравнивать elements и способ менять их местами. Вот как сделать срез пользовательских структур сортируемым:
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 и реализуете несколько стратегий сортировки, что позволит сортировать одну и ту же collection разными способами.
Вы организуете код в двух файлах:
product.go: определите тип product и реализации сортировки.Создайте структуру
Productс тремя полями:Name(string),Price(float64) иQuantity(int).Создайте два именованных типа на основе
[]Product:ByPrice— для сортировки продуктов по цене в ascending порядкеByQuantity— для сортировки продуктов по количеству в descending порядке (сначала наибольшее количество)
Каждый тип должен реализовать три метода, требуемых
sort.Interface:Len(),Less(i, j int)иSwap(i, j int). МетодLessопределяет порядок сортировки для каждого типа.main.go: создайте и отсортируйте inventory продуктов.Прочитайте режим сортировки (
priceилиquantity), затем прочитайте count и сведения о продуктах. Каждый продукт задаётся тремя строками: имя, цена и количество.Создайте срез продуктов, отсортируйте его с помощью соответствующего типа сортировки на основе режима, затем выведите каждый продукт в следующем формате:
[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"
"strings"
)
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 = strings.TrimSpace(name)
priceStr, _ := reader.ReadString('\n')
priceStr = strings.TrimSpace(priceStr)
price, _ := strconv.ParseFloat(priceStr, 64)
qtyStr, _ := reader.ReadString('\n')
qtyStr = strings.TrimSpace(qtyStr)
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