Шаг 51.
Язык программирования Go.
Сортировка и поиск
На этом шаге рассмотрим примеры сортировки срезов и поиска значений.
Задание 1. Сравнить результаты сортировки строк библиотечной функцией sort.Strings и пользовательской SortFoldedStrings (рис.1):
Рис.1. Пример работы приложения
Раскрыть/скрыть решение и комментарии.
Строки сортируются по значениям байт, представляющих их, это означает, например, что сортировка строк выполняется с учетом регистра символов.
Функция sort.Strings() из стандартной библиотеки принимает срез типа []string и сортирует строки на месте, в порядке возрастания по значениям составляющих их байт. Если все строки закодированы с применением одного и того же способа отображения символов в байты (например, все они были созданы текущей программой или другой программой на языке Go), строки будут отсортированы по кодовым пунктам.
Пользовательская функция SortFoldedStrings() действует точно так же, за исключением того, что она сортирует строки без учета регистра символов, используя универсальную функцию sort.Sort().
Функция sort.Sort() способна сортировать элементы любого типа, предоставляющего методы, объявляемые интерфейсом sort.Interface, то есть элементы, имеющие методы Len(), Less() и Swap() с требуемыми сигнатурами.
Приведем пример пользовательской функции:
/*Функция SortFoldedStrings() просто вызывает функцию sort.Sort() из стандартной
библиотеки, преобразуя указанный срез типа []string в значение типа FoldedStrings
с помощью стандартного синтаксиса преобразования. Вообще говоря, всякий раз,
когда определяется пользовательский тип, основанный на встроенном типе,
значение встроенного типа можно преобразовать в значение пользовательского типа,
используя простой синтаксис преобразования.*/
func SortFoldedStrings(slice []string) {
sort.Sort(FoldedStrings(slice))
}
/*Тип FoldedStrings предоставляет три метода, объявляемые интерфейсом sort.Interface.
Все методы имеют очень простую реализацию. Нечувствительность к регистру символов
в этом типе достигается за счет использования функции strings.ToLower() в методе
Less(). Если потребуется реализовать сортировку в порядке возрастания, достаточно
просто заменить в методе Less() оператор < на оператор >.
type FoldedStrings []string
func (slice FoldedStrings) Len() int { return len(slice) }
func (slice FoldedStrings) Less(i, j int) bool {
return strings.ToLower(slice[i]) < strings.ToLower(slice[j])
}
func (slice FoldedStrings) Swap(i, j int) {
slice[i], slice[j] = slice[j], slice[i]
}
Архив примера можно взять здесь.
Задание 2. Реализовать поиск определенного значения в срезе (рис.2):
Рис.2. Пример работы приложения
Раскрыть/скрыть решение и комментарии.
files := []string{"Test.conf", "util.go", "Makefile", "misc.go", "main.go"}
target := "Makefile"
fmt.Printf("Поиск значения \"%s\" в срезе %q\n", target, files)
for i, file := range files {
if file == target {
fmt.Printf("Результат поиска: \"%s\" в срезе files на позиции [%d]\n",
file, i+1)
break
}
}
Архив примера можно взять здесь.
Простой линейный поиск пригоден только
при работе с несортированными данными и только для небольших
срезов (до нескольких сотен элементов). Но для огромных срезов,
особенно когда поиск требуется выполнить многократно, линейный
алгоритм оказывается весьма неэффективным – для поиска элемента в среднем приходится сравнить с искомым значением половину
элементов.
В языке Go имеется метод sort.Search(), реализующий алгоритм
поиска методом половинного деления: для выполнения поиска он
требует выполнить не более log2(n) сравнений (где n – количество
элементов). Если взглянуть с этой точки зрения, алгоритм линейного
поиска по 1 000 000 элементов требует в среднем выполнить 500 000
сравнений и в худшем случае – 1 000 000 сравнений; а алгоритм поиска методом половинного деления потребует выполнить максимум
20 сравнений, даже в самом худшем случае.
Задание 3. Реализовать поиск значения в срезе методом половтнного деления (рис.3):
Рис.3. Пример работы приложения
Раскрыть/скрыть решение и комментарии.
files := []string{"Test.conf", "util.go", "Makefile", "misc.go", "main.go"}
target := "Makefile"
fmt.Printf("Срез до сортировки: %q\n", files)
sort.Strings(files)
fmt.Printf("Поиск значения \"%s\" в упорядоченном срезе %q\n", target, files)
i := sort.Search(len(files),
func(i int) bool { return files[i] >= target })
if i < len(files) && files[i] == target {
fmt.Printf("Результат поиска: \"%s\" в files на позиции [%d]\n", files[i], i+1)
}
Архив примера можно взять здесь.
Функция sort.Search() принимает два аргумента: длину среза
для поиска и функцию, сравнивающую элемент отсортированного
среза с искомым значением с помощью оператора >= – для срезов,
отсортированных в порядке возрастания, и <= – в порядке убывания. Функция должна образовывать замыкание, то есть она должна
быть определена в области видимости среза, где предстоит выполнить поиск, так как она должна захватывать срез как часть своего
окружения. Функция sort.Search() возвращает значение типа int. Если это значение
меньше длины среза и в данной позиции находится искомый элемент, можно сделать вывод, что искомый элемент найден.
На следующем шаге рассмотрим понятие "отображения" в Go.
Предыдущий шаг
Содержание
Следующий шаг