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

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

Жанры

Программирование на языке 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
, имитирующий поведение оператора переключения.

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

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

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

#Бояръ-Аниме. Газлайтер. Том 37

Володин Григорий Григорьевич
37. История Телепата
Фантастика:
фэнтези
аниме
боевая фантастика
5.00
рейтинг книги
#Бояръ-Аниме. Газлайтер. Том 37

Морской волк. 2-я Трилогия

Савин Владислав
2. Морской волк
Фантастика:
альтернативная история
8.91
рейтинг книги
Морской волк. 2-я Трилогия

Последний Паладин. Том 9

Саваровский Роман
9. Путь Паладина
Фантастика:
попаданцы
аниме
фэнтези
5.00
рейтинг книги
Последний Паладин. Том 9

Мусорщик

Поселягин Владимир Геннадьевич
3. Наемник
Фантастика:
боевая фантастика
космическая фантастика
8.55
рейтинг книги
Мусорщик

Вечный. Книга VII

Рокотов Алексей
7. Вечный
Фантастика:
боевая фантастика
рпг
попаданцы
5.00
рейтинг книги
Вечный. Книга VII

Телохранитель Цесаревны

Зот Бакалавр
5. Герой Империи
Фантастика:
фэнтези
боевая фантастика
5.25
рейтинг книги
Телохранитель Цесаревны

Идеальный мир для Лекаря

Сапфир Олег
1. Лекарь
Фантастика:
фэнтези
юмористическое фэнтези
аниме
5.00
рейтинг книги
Идеальный мир для Лекаря

Варяг

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

Последний рейд

Сай Ярослав
5. Медорфенов
Фантастика:
фэнтези
аниме
5.00
рейтинг книги
Последний рейд

ЖЛ 9

Шелег Дмитрий Витальевич
9. Живой лёд
Фантастика:
фэнтези
боевая фантастика
5.00
рейтинг книги
ЖЛ 9

Кадет Морозов

Шелег Дмитрий Витальевич
4. Живой лёд
Фантастика:
боевая фантастика
5.72
рейтинг книги
Кадет Морозов

Мечник Вернувшийся 1000 лет спустя. Том 2

Ткачев Андрей Юрьевич
2. Вернувшийся мечник
Фантастика:
аниме
фэнтези
попаданцы
5.00
рейтинг книги
Мечник Вернувшийся 1000 лет спустя. Том 2

Темный мир

Алмазов Игорь
6. Жизнь Лекаря с нуля
Фантастика:
альтернативная история
аниме
попаданцы
5.00
рейтинг книги
Темный мир