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

на главную

Жанры

Программирование на языке 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. Путь Паладина
Фантастика:
фэнтези
попаданцы
аниме
5.00
рейтинг книги
Последний Паладин

Судебные речи

Вышинский Андрей Януарьевич
Научно-образовательная:
политика
юриспруденция
5.00
рейтинг книги
Судебные речи

Призрак дракона

Бриггз Патриция
Фантастика:
фэнтези
7.29
рейтинг книги
Призрак дракона

Империя кровавого заката

Вэй Алекс
Фантастика:
фэнтези
5.00
рейтинг книги
Империя кровавого заката

Дважды одаренный. Том II

Тарс Элиан
2. Дважды одаренный
Фантастика:
городское фэнтези
альтернативная история
аниме
5.40
рейтинг книги
Дважды одаренный. Том II

Безвременье

Колупаев Виктор Дмитриевич
Фантастика:
социально-философская фантастика
5.00
рейтинг книги
Безвременье

Любовь и магия (сборник)

Гаврилова Анна Сергеевна
Колдовские миры
Фантастика:
фэнтези
мистика
8.38
рейтинг книги
Любовь и магия (сборник)

Красавица Альмиу

Мак Иван
23. Легенды Вселенной
Фантастика:
боевая фантастика
5.00
рейтинг книги
Красавица Альмиу

В боях за Поднебесную. Русский след в Китае

Окороков Александр Васильевич
Военные тайны XX века
Научно-образовательная:
военная история
5.00
рейтинг книги
В боях за Поднебесную. Русский след в Китае

Древо жизни. Книга 3

Кузьменко Владимир Леонидович
3. Древо жизни
Фантастика:
социально-философская фантастика
7.50
рейтинг книги
Древо жизни. Книга 3

Сказки. Истории

Андерсен Ганс Христиан
66. Библиотека всемирной литературы
Проза:
классическая проза
7.38
рейтинг книги
Сказки. Истории

Бортнянский

Ковалев Константин Петрович
748. Жизнь замечательных людей
Документальная литература:
биографии и мемуары
5.00
рейтинг книги
Бортнянский

Рассвет русского царства 3

Грехов Тимофей
3. Новая Русь
Фантастика:
историческое фэнтези
альтернативная история
5.00
рейтинг книги
Рассвет русского царства 3

Город

Загорцев Андрей Владимирович
Проза:
военная проза
8.75
рейтинг книги
Город