Menu
Coddy logo textTech

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().

challenge icon

Задание

Легко

Давайте создадим систему инвентаризации товаров с возможностью сортировки, которая продемонстрирует мощь интерфейса 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 для форматирования цены
}
quiz iconПроверьте себя

В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.

Все уроки раздела Объектно-ориентированное программирование