Чтение онлайн

на главную - закладки

Жанры

Программирование на языке Ruby
Шрифт:

Данный пример не слишком полезен и, конечно, его можно было бы записать более компактно. Но идея в том, что для сравнения двух операндов в определенном порядке над ними можно выполнять произвольно сложный набор операций. (Отметим, однако, что мы не изменили исходные операнды, так как работали с их копиями.) Эта общая техника полезна во многих ситуациях, например для сортировки по нескольким ключам или по ключам, вычисляемым во время выполнения.

В последних версиях Ruby в модуль

Enumerable
добавлен метод
sort_by
(который, конечно,
подмешивается к классу
Array
). Важно понимать, что он делает.

В методе

sort_by
применяется то, что программисты на Perl называют преобразованием Шварца — в честь Рэндала Шварца (Randal Schwartz), внесшего немалый вклад в развитие этого языка. Вместо того чтобы сортировать сами элементы массива, мы применяем к ним некоторую функцию и сортируем возвращаемые ей результаты.

В качестве искусственного примера рассмотрим список файлов, который необходимо отсортировать по размеру. Прямолинейный способ выглядит так:

files = files.sort {|x,y| File.size(x) <=> File.size(y) }

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

Во-вторых, при такой сортировке приходится многократно обращаться к диску, а это довольно дорогая операция (по сравнению с операциями в оперативной памяти). Хуже того, одна и та же операция может выполняться несколько раз.

Метод

sort_by
решает обе проблемы. Вот «правильный» способ:

files = files.sort_by {|x| File.size(x) }

Здесь каждый ключ вычисляется ровно один раз, а затем сохраняется в виде пары ключ-данные. Для небольших массивов производительность при таком подходе может даже снизиться, зато код получается более понятным.

Не существует метода

sort_by!
. Но при желании вы можете написать его самостоятельно.

А как обстоит дело с сортировкой по нескольким ключам? Предположим, что имеется массив объектов, который нужно отсортировать по трем атрибутам: имени, возрасту и росту. Из того, что массивы можно сравнивать, следует, что такое решение будет работать:

list = list.sort_by {|x| [x.name, x.age, x.height] }

Конечно, элементы массива могут быть и не такими простыми. Допустимы произвольно сложные выражения.

8.1.6. Выборка из массива по заданному критерию

Иногда нужно найти в массиве один или несколько элементов так, как будто мы опрашиваем таблицу в базе данных. Для этого есть несколько способов; рассмотренные ниже реализованы в подмешанном модуле

Enumerable
.

Метод

detect
находит не больше одного элемента. Он принимает блок (которому элементы передаются последовательно) и возвращает первый элемент, для которого значение блока оказывается равным
true
.

x = [5, 8, 12, 9, 4, 30]

# Найти первый элемент, кратный 6.

x.detect {|e| e % 6 == 0 } #12

# Найти первый элемент, кратный 7.

c.detect {|e| e % 7 == 0 } # nil

Разумеется,

хранящиеся в массиве объекты могут быть произвольно сложными, равно как и условие, проверяемое в блоке.

Метод

find
— синоним
detect
. Метод
find_all
возвращает несколько элементов, а не один-единственный;
select
— синоним
find_all
.

# Продолжение предыдущего примера...

x.find {|e| e % 2 == 0} # 8

x.find_all {|e| e % 2 == 0} # [8, 12, 4, 30]

x.select {|e| e % 2 == 0} # [8, 12, 4, 30]

Метод

grep
вызывает оператор сравнения (то есть оператор ветвящегося равенства) для сопоставления каждого элемента с заданным образцом. В простейшей форме он возвращает массив, состоящий из элементов, соответствующих образцу. Так как используется оператор
===
, то образец не обязан быть регулярным выражением. (Имя
grep
пришло из UNIX и связано с командой старого редактора
g/re/p
.)

а = %w[January February March April May]

a.grep(/ary/} # ["January, "February"]

b = [1, 20, 5, 7, 13, 33, 15, 28]

b.grep(12..24) # [20, 13, 15]

Существует также блочная форма, которая позволяет преобразовать каждый результат перед записью в массив. Получающийся в результате массив содержит значения, возвращенные блоком, а не те, что были в блок первоначально переданы:

# продолжение предыдущего примера...

# Будем сохранять длины строк.

a.grep(/ary/) {|m| m.length} # [7, 8]

# Будем сохранять квадраты исходных элементов.

b.grep(12..24) { |n| n*n} # {400, 169, 225}

Метод

reject
— полная противоположность
select
. Он исключает из массива элементы, для которых блок возвращает значение
true
. Имеется также вариант
reject!
для модификации массива «на месте»:

с = [5, 8, 12, 9, 4, 30]

d = с.reject {|e| е % 2 == 0} # [5, 9]

b.reject! {|e| е % 3 == 0}

# с равно [5, 8, 4]

Методы

min
и
max
ищут минимальное и максимальное значение в массиве. У каждого метода есть две формы. В первой используется сравнение «по умолчанию», что бы это ни означало в конкретной ситуации (на базе оператора
<=>
). Во второй форме применяется блок для выполнения нестандартного сравнения.

Поделиться:
Популярные книги

Курсант: назад в СССР

Дамиров Рафаэль
1. Курсант
Фантастика:
попаданцы
альтернативная история
7.33
рейтинг книги
Курсант: назад в СССР

На границе империй. Том 7. Часть 2

INDIGO
8. Фортуна дама переменчивая
Фантастика:
космическая фантастика
попаданцы
6.13
рейтинг книги
На границе империй. Том 7. Часть 2

Старшеклассник без клана. Апелляция кибер аутсайдера 2

Афанасьев Семен
2. Старшеклассник без клана. Апелляция аутсайдера
Фантастика:
попаданцы
аниме
5.00
рейтинг книги
Старшеклассник без клана. Апелляция кибер аутсайдера 2

Блуждающие огни

Панченко Андрей Алексеевич
1. Блуждающие огни
Фантастика:
боевая фантастика
космическая фантастика
попаданцы
5.00
рейтинг книги
Блуждающие огни

Отморозок 4

Поповский Андрей Владимирович
4. Отморозок
Фантастика:
попаданцы
фантастика: прочее
5.00
рейтинг книги
Отморозок 4

Газлайтер. Том 9

Володин Григорий
9. История Телепата
Фантастика:
фэнтези
попаданцы
5.00
рейтинг книги
Газлайтер. Том 9

Законы Рода. Том 13

Мельник Андрей
13. Граф Берестьев
Фантастика:
аниме
фэнтези
5.00
рейтинг книги
Законы Рода. Том 13

Сирийский рубеж 3

Дорин Михаил
7. Рубеж
Фантастика:
попаданцы
альтернативная история
5.00
рейтинг книги
Сирийский рубеж 3

Черный Маг Императора 15

Герда Александр
15. Черный маг императора
Фантастика:
юмористическое фэнтези
попаданцы
аниме
сказочная фантастика
фэнтези
фантастика: прочее
5.00
рейтинг книги
Черный Маг Императора 15

Черный дембель. Часть 4

Федин Андрей Анатольевич
4. Черный дембель
Фантастика:
попаданцы
альтернативная история
5.00
рейтинг книги
Черный дембель. Часть 4

Чужак из ниоткуда 3

Евтушенко Алексей Анатольевич
3. Чужак из ниоткуда
Фантастика:
космическая фантастика
альтернативная история
5.00
рейтинг книги
Чужак из ниоткуда 3

Сильнейший Столп Империи. Книга 4

Ермоленков Алексей
4. Сильнейший Столп Империи
Фантастика:
фэнтези
аниме
фантастика: прочее
попаданцы
5.00
рейтинг книги
Сильнейший Столп Империи. Книга 4

Печать мастера

Лисина Александра
6. Гибрид
Фантастика:
попаданцы
технофэнтези
аниме
фэнтези
6.00
рейтинг книги
Печать мастера

За Горизонтом

Вайс Александр
8. Фронтир
Фантастика:
боевая фантастика
космическая фантастика
космоопера
5.00
рейтинг книги
За Горизонтом