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

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

Жанры

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

6.2. Диапазоны

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

digits = 0..9

scalel = 0..10

scale2 = 0...10

Оператор

..
включает конечную точку, а оператор
...
не включает. (Если это вас неочевидно, просто запомните.) Таким образом, диапазоны
digits
и
scale2
из предыдущего
примера одинаковы.

Но диапазоны могут состоять не только из целых чисел — более того, не только из чисел. Началом и концом диапазона в Ruby может быть любой объект. Однако, как мы вскоре увидим, не все диапазоны осмыслены или полезны.

Основные операции над диапазоном — обход, преобразование в массив, а также выяснение, попадает ли некоторый объект в данный диапазон. Рассмотрим разнообразные варианты этих и других операций.

6.2.1. Открытые и замкнутые диапазоны

Диапазон называется замкнутым, если включает конечную точку, и открытым — в противном случае:

r1 = 3..6 # Замкнутый.

r2 = 3...6 # Открытый.

a1 = r1.to_a # [3,4,5,6]

а2 = r2.to_a # [3,4,5]

Нельзя сконструировать диапазон, который не включал бы начальную точку. Можно считать это ограничением языка.

6.2.2. Нахождение границ диапазона

Методы

first
и
last
возвращают соответственно левую и правую границу диапазона. У них есть синонимы
begin
и
end
(это еще и ключевые слова, но интерпретируются как вызов метода, если явно указан вызывающий объект).

r1 = 3..6

r2 = 3...6

r1a, r1b = r1. first, r1.last # 3,6

r1c, r1d = r1.begin, r1.end # 3,6

r2a, r2b = r1.begin, r1.end # 3,6

Метод

exclude_end?
сообщает, включена ли в диапазон конечная точка:

r1.exclude_end? # false

r2.exclude_end? # true

6.2.3. Обход диапазона

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

succ
(следующий).

(3..6).each {|x| puts x } # Печатаются четыре строки

# (скобки обязательны).

Пока все хорошо. И тем не менее будьте очень осторожны при работе со строковыми диапазонами! В классе

String
имеется метод
succ
, но он не слишком полезен. Пользоваться этой возможностью следует только при строго контролируемых условиях, поскольку метод
succ
определен не вполне корректно. (В определении используется, скорее, «интуитивно очевидный», нежели лексикографический порядок, поэтому существуют строки, для которых «следующая» не имеет смысла.)

r1 = "7".."9"

r2 = "7".."10"

r1.each {|x| puts x } #
Печатаются три строки.

r2.each {|x| puts x } # Ничего не печатается!

Предыдущие примеры похожи, но ведут себя по-разному. Отчасти причина в том, что границы второго диапазона — строки разной длины. Мы ожидаем, что в диапазон входят строки

"7"
,
"8"
,
"9"
и
"10"
, но что происходит на самом деле?

При обходе диапазона

r2
мы начинаем со значения
"7"
и входим в цикл, который завершается, когда текущее значение окажется больше правой границы. Но ведь
"7"
и
"10"
— не числа, а строки, и сравниваются они как строки, то есть лексикографически. Поэтому левая граница оказывается больше правой, и цикл не выполняется ни разу.

А что сказать по поводу диапазонов чисел с плавающей точкой? Такой диапазон можно сконструировать и, конечно, проверить, попадает ли в него конкретное число. Это полезно. Но обойти такой диапазон нельзя, так как метод

succ
отсутствует.

fr = 2.0..2.2

fr.each {|x| puts x } # Ошибка!

Почему для чисел с плавающей точкой нет метода

succ
? Теоретически можно было бы увеличивать число на некоторое приращение. Но величина такого приращения сильно зависела бы от конкретной машины, при этом даже для обхода «небольшого» диапазона понадобилось бы гигантское число итераций, а полезность такой операции весьма сомнительна.

6.2.4. Проверка принадлежности диапазону

Зачем нужен диапазон, если нельзя проверить, принадлежит ли ему конкретный объект? Эта задача легко решается с помощью метода

include?
:

r1 = 23456..34567

x = 14142

y = 31416

r1.include?(x) # false

r1.include?(у) # true

У этого метода есть также синоним

member?
.

А как он работает? Как интерпретатор определяет, принадлежит ли объект диапазону? Просто путем сравнения с границами (поэтому проверка принадлежности диапазону возможна лишь, если определен осмысленный оператор

<=>
). Следовательно, запись
(a..b).include?(x)
эквивалентна
x >= a and x <= b
. Еще раз предупреждаем: будьте осторожны со строковыми диапазонами!

s1 = "2".."5"

str = "28"

s1.include?(str) # true (неправильно!)

6.2.5. Преобразование в массив

Когда диапазон преобразуется в массив, интерпретатор последовательно вызывает метод

succ
, пока не будет достигнута правая граница, и помещает каждый элемент диапазона в возвращаемый массив:

r = 3..12

arr = r.to_a # [3,4,5,6,7,8,9,10,11,12]

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

Вперед в прошлое 10

Ратманов Денис
10. Вперед в прошлое
Фантастика:
попаданцы
альтернативная история
5.00
рейтинг книги
Вперед в прошлое 10

Этот мир не выдержит меня. Том 3

Майнер Максим
3. Первый простолюдин в Академии
Фантастика:
фэнтези
попаданцы
5.00
рейтинг книги
Этот мир не выдержит меня. Том 3

Кукловод

Злобин Михаил
2. О чем молчат могилы
Фантастика:
боевая фантастика
8.50
рейтинг книги
Кукловод

Альбион сгорит!

Зот Бакалавр
10. Герой Империи
Фантастика:
фэнтези
попаданцы
аниме
5.00
рейтинг книги
Альбион сгорит!

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

INDIGO
Вселенная EVE Online
Фантастика:
космическая фантастика
5.00
рейтинг книги
На границе империй. Том 10. Часть 2

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

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

Вперед в прошлое 6

Ратманов Денис
6. Вперед в прошлое
Фантастика:
попаданцы
альтернативная история
5.00
рейтинг книги
Вперед в прошлое 6

Темные тропы и светлые дела

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

Звездная Кровь. Экзарх I

Рокотов Алексей
1. Экзарх
Фантастика:
боевая фантастика
рпг
фэнтези
фантастика: прочее
попаданцы
5.00
рейтинг книги
Звездная Кровь. Экзарх I

Эволюционер из трущоб. Том 8

Панарин Антон
8. Эволюционер из трущоб
Фантастика:
попаданцы
аниме
фэнтези
фантастика: прочее
5.00
рейтинг книги
Эволюционер из трущоб. Том 8

Наследник старого рода

Шелег Дмитрий Витальевич
1. Живой лёд
Фантастика:
фэнтези
8.19
рейтинг книги
Наследник старого рода

Магнат

Шимохин Дмитрий
4. Подкидыш
Фантастика:
попаданцы
альтернативная история
5.00
рейтинг книги
Магнат

Переиграть войну! Пенталогия

Рыбаков Артем Олегович
Переиграть войну!
Фантастика:
героическая фантастика
альтернативная история
8.25
рейтинг книги
Переиграть войну! Пенталогия

Светлая тьма. Советник

Шмаков Алексей Семенович
6. Светлая Тьма
Фантастика:
юмористическое фэнтези
городское фэнтези
аниме
сказочная фантастика
фэнтези
5.00
рейтинг книги
Светлая тьма. Советник