На этом шаге мы рассмотрим особенности использования операции диапазон.
Бинарная операция диапазон ".." по существу представляет две различных операции в зависимости от контекста, в котором она используется. В списковом контексте, если ее операндами являются числа (числовые литералы, переменные или выражения, возвращающие числовые значения), она возвращает список, состоящий из последовательности увеличивающихся на единицу целых чисел, начинающихся со значения, определяемого левым операндом, и не превосходящих числовое значение, представленное правым операндом. Операцию диапазон часто используют для задания значений элементов массивов и хешей, а также их фрагментов. Она удобна для организации циклов for и foreach:
#! perl -w # Напечатает числа от 1 до 5, каждое на новой строке. foreach $cycle (1..5) { print "$cycle\n"; } # Напечатает строку "12345". for (1..5) { print; }
Результат выполнения этого примера представлен на рисунке 1.
Рис.1. Результат выполнения примера
Если левый операнд больше правого операнда, то операция диапазон возвращает пустой список. Подобную ситуацию можно отследить с помощью функции defined(), возвращающей истину, если ее параметр определен, или простой проверкой логической истинности массива, элементам которого присваивались значения с помощью операции диапазон:
#! perl -w $min = 2; $max = -2; @array = ($min .. $max); # Массив не определен. print "@array array\n" if defined (@array); # Печати не будет! print "@array array\n" if @array; # Печати не будет!
(-5 .. 5) # Список чисел: (-2, -1, 0, 1, 2). (-5 .. -10) # Пустой список.
Если операндами операции диапазон являются строки, содержащие буквенно-цифровые символы, то в списковом контексте эта операция возвращает список строк, расположенных между строками операндов с использованием лексикографического порядка:
@а = ("a".."d") ; # Массив @а: "а", "b", "с", "d" @а = ("01".."31" ); # Массив @а: "01", "02", ... , "30", "31" @а = ("a1".."a4" ); # Массив @а: "a1", "a2", "аЗ", "а4"
Если левый операнд меньше правого, с точки зрения лексикографического порядка, то возвращается единственное значение, равное левому операнду.
В скалярном контексте операция диапазон возвращает булево значение Истина или Ложь. Она работает как переключатель и эмулирует операцию запятая "," пакетного редактора sed и фильтра awk системы Unix, представляющую диапазон обрабатываемых строк этими программами.
Каждая операция диапазон поддерживает свое собственное булево состояние, которое изменяется в процессе ее повторных вычислений по следующей схеме. Она ложна, пока ложным остается значение ее левого операнда. Как только левый операнд становится истинным, операция диапазон переходит в состояние Истина и находится в нем до того момента, как ее правый операнд не станет истинным, после чего операция снова переходит в состояние Ложь. Правый операнд не вычисляется, пока операция находится в состоянии Ложь; левый операнд не вычисляется, пока операция диапазон находится в состоянии Истина.
В состоянии Ложь возвращаемым значением операции является пустая строка, которая трактуется как булева Ложь. В состоянии Истина при повторном вычислении она возвращает следующее порядковое число, отсчет которого начинается с единицы, т.е. как только операция переходит в состояние Истина, она возвращает 1, при последующем вычислении, если она все еще находится в состоянии Истина, возвращается 2 и т. д. В момент, когда операция переходит в состояние Ложь, к ее последнему возвращаемому порядковому числу добавляется строка "E0", которая не влияет на возвращаемое значение, но может быть использована для идентификации последнего элемента в диапазоне вычислений. Программа следующего примера и ее вывод, представленный на рисунке 2, иллюстрируют поведение оператора диапазон в скалярном контексте. Мы настоятельно рекомендуем внимательно с ними ознакомиться, чтобы "почувствовать", как работает эта операция.
#! perl -w $left = 3; # Операнд1 $right = 2; # Операнд2 # Заголовок таблицы print "\$i\tДиапазон\tOnepaнд1\tOnepaнд2\n"; print '-' x 48, "\n\n"; # Тест операции for($i = 1; $i <= 10; $i++) { $s = $left..$right; print "$i\t $s\t\t $left \t\t $right\n"; $right = 3 if $i==5; # Когда переменная цикла $i равна 5, # $right устанавливается равной 3. if ($right==0) {} else {--$right}; # Уменьшение $right на 1, пока # $right не достигла значения 0. --$left; }
Рис.2. Результат выполнения примера
Сделаем замечания относительно работы программы вышеприведенного примера. На первом шаге цикла левый операнд операции диапазон истинен, следовательно сама операция находится в состоянии Истина и возвращает первое порядковое число (1). Но правый операнд становится также истинным ($right = 2), следовательно она переходит в состояние Ложь и к возвращаемому ей значению добавляется строка "Е0". На втором шаге цикла левый операнд истинен ($ieft = 2) и операция переходит в состояние Истина, возвращая значение 1, к которому опять добавляется строка "Е0", так как истинный правый операнд ($right = 1) переводит операцию в состояние Ложь.
На третьем шаге операция становится истинной ($left = 1), возвращая 1, и правый операнд со значением Ложь ($right = 0) не влияет на ее состояние. На следующих шагах 4 и 5 правый операнд остается ложным, а операция возвращает соответственно следующие порядковые числа 2 и 3. На шаге 6 операция находится в состоянии Истина и возвращает 4, но правый операнд, принимая значение Истина ($right = 0), переводит ее в состояние Ложь, в котором к возвращаемому значению добавляется строка "Е0" и т. д.
Подобное поведение, связанное с переходом из состояния Истина в состояние Ложь и одновременным изменением возвращаемого значения (добавлением строки "Е0") эмулирует поведение операции запятая фильтра awk. Для эмуляции этой же операции редактора sed, в которой изменение возвращаемого значения осуществляется при следующем вычислении операции диапазон, следует вместо двух точек в знаке операции ".." использовать три точки "...". Результаты вывода программы предыдущего примера, в которой осуществлена подобная замена, представлены на рисунке 3.
Рис.3. Результат замены двух точек на три
Еще одно достаточно полезное свойство операции диапазон в скалярном контексте, используемое при обработке строк файлов, заключается в том, что если какой-либо операнд этой операции задан в виде числового литерала, то он сравнивается с номером прочитанной строки файла, хранящейся в специальной переменной $, возвращая булево значение Истина при совпадении и Ложь в противном случае. В программе следующего примера иллюстрируется такое использование операции диапазон. В ней осуществляется пропуск первых не пустых строк файла, печатается первая строка после пустой строки и после этого завершается цикл обработки файла.
#! perl -w open(POST, "file.txt") or die "Нельзя открыть файл file.txt!"; LINE : while(<POST>) { $temp = 1../^$/; # Истина, пока строка файла не пустая. next LINE if ($temp); # Переход на чтение новой строки, # если $temp истинна. print $_; # Печать первой строки файла после не пустой last; # Выход из цикла } close(POST);
В этой программе для нас интересен оператор присваивания возвращаемого значения операции диапазон переменной $temp. Прежде всего отметим, что эта операция используется в скалярном контексте, причем ее левый операнд представлен числовым литералом. На первом шаге цикла читается первая строка файла и специальной переменной $, присваивается ее номер 1, который сравнивается со значением левого операнда операции диапазон. Результатом этого сравнения является булево значение Истина, и операция диапазон переходит в состояние Истина, в котором она и остается, если первая строка файла не пустая, так как операция поиска по образцу /^$/ возвращает в этом случае Ложь. Операция next осуществляет переход к следующей итерации цикла, во время которой читается вторая строка файла. Операция диапазон остается в состоянии Истина, если прочитанная строка файла пустая, увеличивая возвращаемое значение на единицу. Далее операция next снова инициирует чтение новой строки файла. Если строка файла пустая, то операция поиска по образцу возвращает значение Истина, переводя тем самым операцию диапазон в состояние Ложь, которое распознается при следующем ее вычислении, поэтому считывается еще одна строка файла (первая после пустой). Теперь операция next не выполняется, так как операция диапазон возвращает Ложь, печатается первая не пустая строка и операция last прекращает дальнейшую обработку файла.
На следующем шаге мы рассмотрим операцию запятая.