logo
Программирование на языке Ruby

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