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

на главную

Жанры

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

Ясно, что для диапазонов чисел типа

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

6.2.6. Обратные диапазоны

Имеет ли смысл говорить об обратном диапазоне? И да, и нет. Следующий диапазон допустим:

r = 6..3

x = r.begin # 6

y = r.end # 3

flag = r.end_excluded? # false

Как видите, мы можем

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

arr = r. to_a # []

r.each {|x| p x } # Ни одной итерации.

y = 5

r.include?(у) # false (для любого значения y)

Означает ли это, что обратные диапазоны всегда бесполезны? Вовсе нет. В некоторых случаях разумно инкапсулировать границы в один объект.

На самом деле массивы и строки часто принимают обратные диапазоны в качестве индексов, поскольку индексация для них начинается с 0, если отсчитывать от левой границы, и с -1 — если от правой. Поэтому допустимы такие выражения:

string = "flowery

str1 = string[0..-2] # "flower"

str2 = string[1..-2] # "lower"

str3 = string[-5..-3] # "owe" (по существу, прямой диапазон)

6.2.7. Оператор переключения

Диапазон в составе условия обрабатывается особым образом. В этом случае

..
называется оператором переключения (flip-flop operator), поскольку это, по существу, переключатель, который сохраняет свое состояние.

Такой прием, позаимствованный из языка Perl, бывает полезен. Но понять, как он работает, довольно трудно.

Представьте себе исходный текст программы на Ruby, в который встроена документация, ограниченная маркерами

=begin
и
=end
. Как бы вы подошли к задаче отыскания и вывода этих и только этих фрагментов? (Состояние переключается между «внутри раздела» и «вне раздела», отсюда и понятие переключения.) Решение, хотя интуитивно и не очевидное, дает следующий код:

loop do

 break if eof?

 line = gets

 puts line if (line=~/=begin/)..(line=~/=end/)

end

«Волшебство» объясняется принципом работы оператора переключения.

Во-первых, надо осознать, что «диапазон» сохраняет свое состояние, хотя оно и скрыто. Когда становится истинным условие, заданное в качестве левой границы, сам диапазон принимает значение

true
. Он сохраняет это состояние до тех пор пока не станет истинным условие на правой границе, и в этот момент состояние переключается в
false
.

Такое поведение полезно во многих случаях, в частности для разбора HTML-документов или конфигурационных файлов, разбитых на разделы, выбора диапазонов элементов из списков и т.д.

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

Что

меня не устраивает в операторе переключения? В контексте предыдущего примера рассмотрим строку, начинающуюся с маркера
=begin
. Напомним, что оператор
=~
не возвращает
true
или
false
, как можно было бы ожидать; он возвращает начальную позицию найденного соответствия (
Fixnum
) или
nil
, если соответствие не найдено. Следовательно, при вычислении выражений для строк, попадающих и не попадающих в диапазон, мы получаем
0
и
nil
соответственно.

Однако при попытке сконструировать диапазон от

0
до
nil
возникает ошибка, поскольку такой диапазон не имеет смысла:

range = 0..nil # Ошибка!

Далее, напомню, что в Ruby только

false
и
nil
дают значение «ложь» — все остальные объекты в логическом контексте вычисляются как «истина». А значит, следуя общей идеологии диапазон не должен вычисляться как «ложь».

puts "hello" if x..y

# Печатается "hello" для любого допустимого диапазона x..y.

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

true
.

loop do

 break if eof?

 line = gets

 start = line=~/=begin/

 stop = line=~/=end/

 puts line if start..stop

end

А что если сам диапазон поместить в переменную? Тоже не получится — проверка снова дает

true
.

loop do

 break if eof?

 line = gets

 range = (line=~/=begin/)..(line=~/=end/)

 puts line if range

end

Чтобы понять, в чем дело, нужно осознать, что весь диапазон (включая обе границы) вычисляется на каждой итерации цикла, но с учетом внутреннего состояния. Поэтому оператор переключения — вообще не настоящий диапазон. Тот факт, что он выглядит похожим на диапазон, хотя по сути таковым не является, многие считают «злом».

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

proc
. Это не значения, а исполняемый код. Тот факт, что нечто, выглядящее как обычное выражение, на самом деле представляет собой
proc
, тоже не вызывает восторга.

И несмотря на все вышесказанное, функциональность-то полезная!.. Можно ли написать класс, который инкапсулирует ее, но при этом не будет таким «магическим»? Можно и даже не очень трудно. В листинге 6.1 приведен простой класс

Transition
, имитирующий поведение оператора переключения.

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

Кай из рода красных драконов 4

Бэд Кристиан
4. Красная кость
Фантастика:
фэнтези
попаданцы
5.00
рейтинг книги
Кай из рода красных драконов 4

Лекарь Империи 10

Карелин Сергей Витальевич
10. Лекарь Империи
Фантастика:
городское фэнтези
боевая фантастика
аниме
попаданцы
5.00
рейтинг книги
Лекарь Империи 10

Государь

Мазин Александр Владимирович
7. Варяг
Фантастика:
альтернативная история
8.93
рейтинг книги
Государь

Хозяин Теней 7

Петров Максим Николаевич
7. Безбожник
Фантастика:
аниме
фэнтези
фантастика: прочее
попаданцы
5.00
рейтинг книги
Хозяин Теней 7

Князь Андер Арес 5

Грехов Тимофей
5. Андер Арес
Фантастика:
историческое фэнтези
фэнтези
героическая фантастика
5.00
рейтинг книги
Князь Андер Арес 5

Кодекс Охотника. Книга II

Винокуров Юрий
2. Кодекс Охотника
Фантастика:
фэнтези
попаданцы
боевая фантастика
юмористическое фэнтези
5.00
рейтинг книги
Кодекс Охотника. Книга II

Воин

Бубела Олег Николаевич
2. Совсем не герой
Фантастика:
фэнтези
попаданцы
9.25
рейтинг книги
Воин

Отверженный III: Вызов

Опсокополос Алексис
3. Отверженный
Фантастика:
фэнтези
альтернативная история
7.73
рейтинг книги
Отверженный III: Вызов

Я еще не царь

Дрейк Сириус
25. Дорогой барон!
Фантастика:
юмористическое фэнтези
аниме
попаданцы
5.00
рейтинг книги
Я еще не царь

Имя нам Легион. Том 16

Дорничев Дмитрий
16. Меж двух миров
Фантастика:
боевая фантастика
рпг
аниме
5.00
рейтинг книги
Имя нам Легион. Том 16

Император Пограничья 7

Астахов Евгений Евгеньевич
7. Император Пограничья
Фантастика:
аниме
фэнтези
попаданцы
5.00
рейтинг книги
Император Пограничья 7

Андер Арес

Грехов Тимофей
1. Андер Арес
Фантастика:
рпг
аниме
фэнтези
фантастика: прочее
5.00
рейтинг книги
Андер Арес

Убивать чтобы жить 7

Бор Жорж
7. УЧЖ
Фантастика:
героическая фантастика
космическая фантастика
рпг
5.00
рейтинг книги
Убивать чтобы жить 7

Закрытые Миры

Муравьёв Константин Николаевич
Вселенная EVE Online
Фантастика:
фэнтези
5.86
рейтинг книги
Закрытые Миры