Глава 4. Операторы и приоритеты операторов
Коротко
В трех предыдущих главах обсуждались стандартные форматы данных в языке Perl. В этой главе речь идет о работе с данными с помощью операторов. Операторы позволяют совершать операции с данными, даже если это простое сложение:
print 2 + 2;
4
Операторы могут выполнять и более сложные вычисления, подобно тернарному («тройственному») условному оператору, рассматриваемому в следующем примере:
while (<>)
{ print $_ < 10 ? $_ : "${\((a,b,c,d,e,f)[$_ - 10])}\n"; }
Это выражение считывает из входных данных числа от 0 до 15 и выводит шестнадцатеричную цифру (если вы не знакомы с оператором ?:, обратитесь к разделу «Тернарный условный оператор» в этой главе).
Операторы Perl выполняют самые различные действия, но в первом приближении они могут быть разбиты на унарные, бинарные, тернарные и списковые. Унарным операторам требуется один операнд — например, в случае оператора !, выполняющего побитное логическое отрицание , команда $notvariable = !$variable осуществит логическое инвертирование содержимого переменной $variable и занесет результат в переменную $notvariable. Бинарные операторы требуют двух операндов — например, для оператора сложения + команда $sum = 2 + 2 вычислит сумму двух целых чисел и занесет ее в переменную $sum. Тернарные операторы используют три операнда — например, в результате работы условного оператора ?: команда
$absvariable = $variable >= 0 ? $variable : -$variable;
найдет и занесет в переменную $absvariable абсолютное значение выражения $variable. Наконец, списковые операторы , подобно оператору print, используют списки операндов (как, например, команда print 1, 2, 3, 4, 5).
Глава 4. Операторы и приоритеты операторов
Ссылка на функцию print как на оператор на первый взгляд может показаться странной. Но в Perl функция с аргументами, указанными без круглых скобок, рассматривается в качестве оператора. Более того, при вычислении выражений используются правила приоритета, определенные для операторов (более подробно об этом говорится в разделе «Наивысший приоритет: термы и списки, стоящие справа» этой главы). Прежде чем переходить к работе с операторами, необходимо разобраться с понятием сравнительного приоритета.
Приоритет операторов
Операторы Perl по мере убывания приоритета (первая строчка соответствует наивысшему приоритету) перечислены в табл. 4.1.
Время от времени при работе с Perl приходится учитывать сравнительный приоритет операторов. В одном выражении может использоваться несколько операторов:
print 2 + 3 * 4;
Сложит ли Perl 2 и 3 перед умножением на 4 или сперва умножит 3 на 4 и лишь затем прибавит 2? Этот вопрос проясняется при знании правил приоритета операторов, существующих в Perl. Как видно из табл. 4.1, умножение (*) имеет более высокий приоритет, чем сложение (+). Поэтому Perl сначала умножит 3 на 4, а потом прибавит 2:
print 2 + 3 * 4;
14
Естественно, можно изменить порядок выполнения операций, расставив скобки:
print ((2 + 3) * 4);
20
Кстати, обратите внимание, что эту команду нельзя задать как
print (2 + 3) * 4;
Поскольку print работает либо как оператор, либо как функция, скобка указывает Perl, что print надо использовать в качестве функции, а 2 + 3 — это параметр, передаваемый ей. Поэтому Perl послушно выведет:
print (2 + 3) * 4;
5
Однако эту проблему можно обойти. Если интерпретация списочного оператора (того же print) с аргументом, начинающимся со скобки, как вызова функции, не требуется, то перед скобками можно поставить унарный оператор +. Он не окажет никакого воздействия на само выражение, однако сообщит Perl, что интерпретировать конструкцию в скобках как вызов функции не следует:
print +(2 + 3) * 4;
20
Итак, сравнительный приоритет операторов весьма важен. Разделы этой главы будут организованы в соответствии с табл. 4.1, то есть операторы с наивысшим приоритетом обсуждаются в первую очередь.
Таблица 4.1. Сравнительный приоритет операторов
(операторы, перечисленные
в одной группе, имеют одинаковый
приоритет)
Оператор Ассоциативность
термы Левая
правый оператор списка
-> Левая
++ Не определена
--
** Правая
! Правая
~
\
унарный +
унарный _
=~ Левая
!~
* Левая
/
%
x
+ Левая
_
.
<< Левая
>>
именованные унарные Не
определена
операторы,
операторы проверки файлов
<> Не
определена
<=
>=
lt
gt
le
ge
== Не определена
!=
<=>
eq
ne
cmp
& Левая
| Левая
^
&& Левая
|| Левая
продолжение И
Таблица 4.1 (продолжение )
Оператор Ассоциативность
.. Не определена
...
?: Правая
=
Правая
+=
-=
*=
/=
%=
&=
|=
^=
.=
x=
**=
<<=
>>=
&&=
||=
, Левая
=>
левый оператор списка Не определена
not Правая
and Левая
or Левая
xor
Непосредственные решения
Наивысший приоритет: термы и списки, стоящие справа
Термы имеют в Perl наивысший приоритет. К термам относятся переменные, выражения в кавычках, выражения в скобках, конструкции do и eval, безымянные (анонимные) массивы и хэши, создаваемые с помощью конструкций [] и {} (см. главу 8), а также функции с аргументами, заключенными в скобки.
Точно так же наивысший приоритет, направленный влево (leftward precedence), имеет оператор списка (набор элементов, разделенных запятой). Это значит, что по сравнению с операторами, находящимися слева от него, он имеет наивысший приоритет, а когда его приоритет сравнивается с операторами, стоящими справа, этот же оператор списка получит гораздо более слабый правый приоритет (rightward precedence).
Рассмотрим следующий пример:
print 1, 2, 3, 4, sort 9, 8, 7, 6, 5;
123456789
Как видно из результата, сперва оператор sort сортирует список, указанный в качестве его аргумента, а затем print выводит список, являющийся объединением двух списков. Когда идет анализ числа 9, то слева от него оказывается оператор sort, а справа — оператор списка. Так как по отношению к левой стороне у оператора списка больший приоритет, то Perl сперва объединит число 9 в один список с последующими элементами (см. также раздел «Списки, стоящие слева» ближе к концу главы) и лишь затем применит к результату операцию сортировки. С другой стороны, когда Perl доходит до числа 4, то имеет дело с двумя идущими подряд операторами — оператором списка и оператором sort. Оператор списка имеет более слабый приоритет по отношению к расположенному справа от него оператору sort, поэтому объединение элементов в список будет отложено до тех пор, пока оператор сортировки не выполнит свою работу.
Оператор-стрелка
Инфиксный оператор ->, разыменующий ссылку, заимствован из языка С. Если справа от него находится конструкция [...] или {...}, то слева должна находиться ссылка на массив или хэш. В следующем примере создается ссылка на хэш (с помощью оператора \, возвращающего адрес переменной), а затем с помощью оператора -> обеспечивается доступ к содержимому хэша через полученную ссылку:
$hash{fruit} = apple;
$hash{sandwich} = hamburger;
$hash{drink} = bubbly;
$hashref = \%hash;
print $hashref -> {sandwich};
hamburger
Когда справа от оператора нет квадратных [...] или фигурных {...} скобок, а левая часть не представляет собой ссылки на массив или хэш, то слева должен находиться либо объект, либо имя класса, а справа — его метод:
$result = $myobject -> mymethod{$data);
(Более подробно об объектах и классах рассказывается в главе 16.)
Автоприращение и автоуменьшение
Оператор приращения (++) и оператор уменьшения (__) в Perl работают точно так же, как в языке С. Находясь перед именем переменной, они увеличивают или уменьшают ее значение на единицу до того, как подставить значение в выражение. Когда эти операторы оказываются после имени переменной, они сперва подставляют в выражение значение переменной, а потом увеличивают или уменьшают ее содержимое.
Допустим, определены переменные $variable1 и $variable2:
$variable1 = 1;
$variable2 = 1;
Увеличить значение переменной можно, применив оператор ++ в качестве префикса:
print ++$variable1 . "\n";
print $variable1 . "\n";
2
2
Если тот же оператор использовать в качестве суффикса, значение переменной изменится лишь после того, как оператор вернет ее текущее значение:
print $variable2++ . "\n";
print $variable2 . "\n";
1
2
Отметим, что оператор работает также и с текстовыми строками, хранящимися в скалярных переменных (однако только когда скалярная переменная не используется в числовом контексте). Например, программа
$variable = 'AAA';
print ++$variable . "\n";
$variable = 'bbb';
print ++$variable . "\n";
$variable = 'zzz';
print ++$variable . "\n";
выводит результат:
AAB
bbc
aaaa
Оператор связывания
Возведение в степень
Оператор возведения в степень выглядит как **. Это бинарный оператор, который возводит первый аргумент в степень, указанную вторым аргументом. Например:
print 2 ** 10'
1024
Унарные операторы
В Perl есть четыре унарных оператора:
· ! — логическое отрицание (логическая операция not).
· _ — арифметическое отрицание (унарный минус).
· + — пустая арифметическая операция (унарный плюс).
· ~ — побитное отрицание (побитное дополнение до единицы).
· \ — создание ссылки (вычисление адреса объекта).
Например, логическое отрицание числа ноль есть единица:
print !0;
1
Побитное же отрицание инвертирует все биты в числе. В частности, операция ~0 позволит определить максимальное, представимое в конкретной системе беззнаковое целое (она установит все двоичные разряды слова, выделенного для числа, в единицы):
print ~0;
4294967295
Отсюда следует, что слово на данной машине имеет 32 разряда, так как 4294967295 = 2**32 _ 1.
Оператор связывания
Оператор связывания =~ связывает скалярную переменную и операцию поиска/замены по шаблону (более подробно эти операции рассматриваются в главе 6). Строковые операторы s/.../.../, m/.../, tr/.../ по умолчанию работают с переменной $_. Оператор связывания =~ используется, чтобы они работали с конкретной скалярной переменной. Например, поместим строку в переменную $line:
$line = ".Hello!";
Теперь выполним поиск по шаблону:
$line = ".Hello!";
if ($line =~ m/^\./)
{ print "Should't start a sentence with a period!"; }
Should't start a sentence with a period!
Оператор !~ работает точно так же, за тем исключением, что он выполняет логическое отрицание над результатом, возвращаемым оператором =~ (как и другие функции, операции поиска и замены возвращают некоторое значение в качестве результата своей работы — более подробно об этом рассказывается в главе 6).
Умножение и деление
Оператор умножения * перемножает два числа:
print 2 * 4;
8
Оператор деления / делит одно число на другое:
print 16 / 3;
5.33333333333333
Оператор % вычисляет остаток от целочисленного деления двух значений:
print 16 % 3;
1
Оператор повторения x дублирует действие заданное количество раз. В скалярном контексте он возвращает строку, полученную из значения, стоящего слева от оператора, путем повторения этого значения (конкатенации с собой) столько раз, сколько задано числом справа от оператора. Например, вот как напечатать строку из 30 дефисов:
print '-` x 30;
------------------------------
Если левый операнд является списком, заключенным в круглые скобки, то оператор повторения продублирует список необходимое число раз. Вот как, например, создать список из восьмидесяти единиц и присвоить его массиву:
@ones = (1) x 80;
Следующий неочевидный пример заменяет все единицы на пятерки:
@ones = (5) x @ones;
(поскольку правый операнд оператора повторения должен быть числом, то массив @ones интерпретируется в скалярном контексте, то есть как целое число, равное текущему количеству элементов массива).
Сложение, вычитание и конкатенация
Оператор сложения (+) складывает два числа:
print 2 + 2;
4
Оператор вычитания (_) вычитает одно число из другого:
print 4 - 2;
2
Операторы проверки файлов
Оператор конкатенации (.) объединяет две строки:
print "Hello " . "there.";
Hello "there.
Все эти операторы являются бинарными (в частности, не надо путать бинарные операторы сложения и вычитания с унарным плюсом и унарным минусом).
Оператор сдвига
Оператор сдвига влево << возвращает значение левого аргумента, сдвинутого влево на число битов, определенное правым аргументом:
print 2 << 10;
2048
Оператор сдвига вправо >> возвращает значение левого аргумента, сдвинутого вправо на число битов, определенное правым аргументом. Например:
print 2048 >> 3;
256
Именованные унарные операторы
Perl рассматривает функции с одним скалярным аргументом (не списком), как именованные унарные операторы, если аргумент не заключен в круглые скобки. (Такими функциями являются, к примеру, sqrt, defined, eval, return, chdir, rmdir, oct, hex, undef, exists и многие другие.) Вот, например, как используется в качестве оператора функция sqrt:
print sqrt 4;
2
Операторы проверки файлов
Perl поддерживает массу операторов для проверки состояния файлов, что позволяет полностью контролировать файлы и их дескрипторы (см. главу 12 относительно работы с файлами в Perl). Вот как записываются эти операторы (X заменяет любой оператор):
-X дескриптор-файла
-X выражение
-X
Вместо X надо подставить один из операторов, приведенных в табл. 4.21. Возможно, какая-то информация из этой таблицы окажется для вас незнакомой, если только вы не работаете с операционной системой Unix. Так, аббревиатуры UID
1 В документации, сопровождающей Perl (файл perlfunc), можно найти и другие операторы, не включенные в табл. 4.2. По большей части их работа сильно зависит от системы (в частности, в Windows многие просто бесполезны, а многие работают неправильно для FAT16 и FAT32). — Примеч. ред.
и GID означают соответственно идентификатор пользователя (user ID) и группы (group ID). Если опущен аргумент, используется переменная $_ (за исключением оператора -t, который по умолчанию работает с входным потоком данных STDIN).
Вот несколько примеров проверки файлового дескриптора STDIN (по умолчанию соответствует входному потоку данных):
print -e STDIN; # Существует ли STDIN?
1
print -t STDIN; # Присоеденен ли он к терминалу?
1
print -z STDIN; # Имеет ли он нулевой размер?
1
(Обратите внимание, что логическое значение истина (true) возвращается этими операторами, как число один.)
Таблица 4.2. Операторы тестирования файлов
Оператор Проверяемая информация
-A Время, прошедшее с момента последнего
обращения к
файлу
-b Блочный файл
-B Двоичный файл
-c Символьный файл
-C Время, прошедшее с момента последнего
изменения
индексного дескриптора
-d Каталог
-e Файл существует
-f Обычный файл
-g Установлен атрибут SETGID
-k Установлен атрибут Sticky bit1
-l Файл является символической ссылкой
-M Время существования файла в днях на момент
запуска
сценария
-o Файл принадлежит текущему пользователю
(effective
UID)
-O Файл принадлежит реальному пользователю
(real UID)
-p Файл является именованным каналом (FIFO)
-r Файл доступен для чтения текущему
пользователю
(effective UID) или группе (effective GID)
-R Файл доступен на чтение реальному пользователю
(real
UID) или группе (real GID)
1 «Липкий бит» устанавливается для каталога и запрещает пользователю, имеющему право на запись в этот каталог, удалять файлы, владельцем которых он не является. Для пользователей системы Unix это понятие знакомо и без перевода, а в операционной системе Windows ничего подобного просто нет. — Примеч. ред.
Операторы сравнения
Оператор Проверяемая информация
-s Размер файла (он же — проверка файла на нулевой размер)
-S Файл является сокетом (сетевым соединением)
-t Файл открыт на текущем терминале
-T Текстовый файл
-u Для файла установлен атрибут SETUID
-w Файл доступен для записи текущему
пользователю
(effective UID) или группе (effective GID)
-W Файл доступен для чтения реальному
пользователю (real
UID) или группе (real GID)
-x Файл доступен для выполнения текущему
пользователю
(effective UID) или группе (effective GID)
-X Файл доступен для выполнения реальному
пользователю
(real UID) или группе (real GID)
-z Файл имеет нулевой размер
Операторы сравнения
Операторы сравнения — это бинарные операторы, которые сравнивают данные, возвращая единицу в качестве значения истина (true) и пустую строку в качестве значения ложь (false). Они перечислены в табл. 4.3. Обратите внимание, что одни операторы используются для сравнения чисел, а другие — для сравнения строк.
Подсказка. Обратите внимание, что оператор «больше или равно» обозначен как >=, чтобы не спутать его с оператором => — синонимом запятой (разделителя элементов списка).
В следующем примере проверяются числа, вводимые пользователем, и, если введенное число оказывается больше ста, выдается сообщение об ошибке:
while (<>) {
if ($_ > 100)
{ print "Too big!\n"; }
}
Вы можете использовать логические операторы && и || или их сородичей and и or (отличающихся меньшим приоритетом) для объединения отдельных логических утверждений. В следующем примере задается требование того, чтобы введенная пользователем буква была между k и m:
print "Please enter letters from k to m\n";
while (<>) {
chop;
if ($_ lt 'k' or $_ gt 'm') {
print "Please enter letters from k to m\n";
} else
{ print "Thank you - let's have another!\n"; }
}
Таблица 4.3. Операторы сравнения
Оператор Тип данных Возвращаемое значение
< Число Истина, если левый операнд меньше правого
> Число Истина, если левый операнд больше правого
<= Число Истина, если левый операнд меньше или равен правому
>= Число Истина, если левый операнд больше или равен правому
lt Строка Истина, если левый операнд меньше правого
gt Строка Истина, если левый операнд больше правого
le Строка Истина, если левый операнд меньше или равен правому
ge Строка Истина, если левый операнд больше или равен правому
Операторы равенства
Кроме рассмотренных в предыдущем разделе операторов сравнения Perl поддерживает операторы проверки на равенство, приведенные в табл. 4.4. (Поскольку они имеют меньший приоритет, чем операторы сравнения, для них отведен особый раздел.) Как и в случае операторов сравнения, существуют отдельные операторы для числовых данных и текстовых строк.
В следующем примере пользователь должен ввести букву y (игрек). Запрос повторяется до бесконечности с выводом сообщения об ошибке, пока пользователь не выполнит требуемое действие:
print "Please type the letter y\n";
while (<>) {
chop;
if ($_ ne 'y') {
print "Please type the letter y\n";
} else {
print "Do you always do what you're told?\n";
exit;
}
}
В результате работы этой программы на выходе может получиться следующий текст:
Please type the letter y
a
Please type the letter y
b
Please type the letter y
c
Please type the letter y
y
Do you always do what you're told?
Побитное «ИЛИ»
Таблица 4.4. Операторы сравнения
Оператор Тип данных Возвращаемое значение
== Число Истина, если левый операнд равен правому
!= Число Истина, если левый операнд не равен правому
<=> Число _1, 0 или 1 в зависимости от того, является
ли
левый операнд меньше правого, равным правому
или
больше правого
eq Строка Истина, если левый операнд равен правому
ne Строка Истина, если левый операнд не равен правому
cmp Строка _1, 0 или 1 в зависимости от того, является ли
левый операнд
меньше правого, равным правому или больше
правого
Побитное «И»
Оператор побитного логического И (&) возвращает результат логического умножения and над битами операндов, выровненных друг относительно друга. (Таблица логического умножения битов приведена в табл. 4.5.)
Например, выполнение этой операции над числами 5 (биты 0 и 2 равны единице) и 4 (только бит 2 равен единице) дает результат 4:
print 5 & 4;
4
Таблица 4.5. Оператор «И»
and 0 1
0 0 0
1 0 1
Побитное «ИЛИ»
Оператор побитного логического ИЛИ (|) возвращает результат логического сложения or над битами операндов, выровненных друг относительно друга. (Таблица логического сложения битов приведена в табл. 4.6.)
Таблица 4.6. Оператор «ИЛИ»
or 0 1
0 0 1
1 1 1
Например, выполнение этой операции над числами 4 (бит 2 равен единице) и 1 (бит 0 равен единице) дает результат 5 (где единице равны биты 0 и 2):
print 4 | 1;
5
Побитное «Исключающее ИЛИ»
Оператор побитного логического ИСКЛЮЧАЮЩЕГО ИЛИ (^) возвращает результат логической операции xor над битами операндов, выровненных друг относительно друга. (Таблица операции xor для битов приведена в табл. 4.7.)
Обратите внимание, что для «исключающего или», если и в первом, и во втором операнде бит равен единице, на выходе получается ноль. Несколько примеров:
print 0 ^ 0;
0
print 1 ^ 0;
1
print 1 ^ 1;
0
print 0 ^ 1;
1
print 5 ^ 4;
1
Таблица 4.7. Оператор «Исключающее ИЛИ»
xor 0 1
0 0 0
1 1 0
Логическое «И» в стиле языка С
Оператор && выполняет логическую операцию И над двумя логическими значениями. Он может объединять два оператора сравнения, проверяя, чтобы оба возвращали значение истина перед тем, как вернуть это значение в качестве результата. Пример:
print "Please enter numbers from 5 to 10\n";
while (<>) {
chop;
if ($_ >= 5 && $_ <= 10) {
print "Thank you - let's have another!\n";
} else
{ print "Please enter numbers from 5 to 10\n"; }
}
Речь идет об «операторе в стиле С», поскольку в данном случае используется тот же самый синтаксис, а кроме того, этот оператор имеет такой же приоритет, как и в C. (В Perl существует и еще один логический оператор аналогичного действия, обозначаемый как and, но его приоритет меньше.) Этот оператор также известен как оператор короткого действия — если левый операнд соответствует условию ложь, то второй операнд не вычисляется и не проверяется.
Логическое «ИЛИ» в стиле языка С
В отличие от С в Perl этот оператор возвращает не ноль или единицу, а последнее вычисленное значение, то есть первый операнд, если он соответствует условию ложь, или второй, если первый соответствует условию истина (напомним, что в Perl условию ложь соответствуют любые пустые значения — пустая строка, неопределенная переменная, undef и т. д., а условию истина — все остальные).
Логическое «ИЛИ» в стиле языка С
Оператор || выполняет логическую операцию ИЛИ над двумя логическими значениями. Он может объединять два оператора сравнения, возвращая значение истина, если хотя бы один из операндов возвращает значение истина. В этом примере печатается сообщение об ошибке, если введенное пользователем число не входит в указанный диапазон:
print "Please enter numbers from 5 to 10\n";
while (<>) {
chop;
if ($_ < 5 || $_ > 10) {
print "Please enter numbers from 5 to 10\n";
} else
{
print "Thank you - let's have another!\n";
}
}
Опять-таки, мы говорим о «стиле С», поскольку в данном случае используется аналогичный синтаксис и аналогичный приоритет. (В Perl есть и другой логический оператор аналогичного действия, обозначаемый как or, но его приоритет меньше.) Как и предыдущий оператор &&, он является оператором короткого действия — если левый операнд соответствует условию истина, то второй операнд не вычисляется и не проверяется.
В отличие от С в Perl этот оператор возвращает не ноль или единицу, а последнее вычисленное значение, то есть первый операнд, если он соответствует условию истина, или второй, если первый соответствует условию ложь (напомним, что в Perl условию ложь соответствуют любые пустые значения — пустая строка, неопределенная переменная, undef и т. д., а условию истина — все остальные).
Тот факт, что оператор || возвращает последнее вычисленное значение, а не значения истина или ложь (именно в таком стиле о нем и надлежит думать при написании программы), позволяет использовать его для самых разных операций. Например, можно испытать несколько вариантов действия и в случае окончатель ной неудачи вывести сообщение об ошибке, прекратив работу:
$result = this($data) || that($data)
|| die "Can't get this() or that() to work\n";
Здесь нормальная последовательность действий прерывается с помощью функции die. Она вызовет выход из программы с выводом сообщения вида «Can't get this() or that() to work at try.pl line X».
Оператор диапазона в списковом контексте
В зависимости от контекста возможны два принципиально разных варианта работы оператора диапазона (..). В списковом контексте он возвращает массив значений с шагом 1, лежащих в диапазоне, указанных левым и правым операндами, начиная со значения, указанного левым операндом. Например, следующая команда выводит одну и ту же строку пять раз подряд:
for (1 .. 5)
{ print "Here we are again!\n"; }
Here we are again!
Here we are again!
Here we are again!
Here we are again!
Here we are again!
То же самое произойдет, если немного уменьшить верхнюю границу диапазона:
for (1 .. 4.5)
{ print "Here we are again!\n"; }
Here we are again!
Here we are again!
Here we are again!
Here we are again!
Here we are again!
(Если верхняя граница не совпадает с последним сгенерированным значением, то оператор диапазона возвращает на один элемент больше — на последнем шаге он включает в список значение, удовлетворяющее условию «больше или равно».)
Необходимо отметить, что в Perl версии 5 для операторов for и foreach подобная конструкция не приводит к выделению промежуточного временного массива. Напротив, в старых версиях Perl при выполнении цикла
for (1 .. 1_000_000_000) {
# код тела цикла
}
вы можете столкнуться с серьезными проблемами нехватки памяти.
В списковом контексте оператор диапазона работает не только с числами, но и со строками (подобно рассмотренным ранее операторам ++ и --). Например, можно использовать команду
@alphabet = ('A' .. 'Z');
чтобы получить список заглавных латинских букв, или команду
@hexdigit = ('0' .. '9', 'a' .. 'f')[$num & 15];
для получения шестнадцатеричной цифры, или команду
@date = ('00' .. '31');
print $date[$day];
чтобы вывести дату с ведущими нулями.
Оператор диапазона в скалярном контексте
Оператор диапазона в скалярном контексте
В скалярном контексте оператор диапазона возвращает логическое значение, соответствующее условию ложь или истина. В отличие от рассмотренных ранее операторов сравнения, каждый оператор диапазона, используемый программой на Perl в скалярном контексте, ведет себя как маленький триггер — у него есть собственное внутреннее состояние, которое учитывается при очередном выполнении данного фрагмента кода.
Рассмотрим, как работает оператор диапазона в скалярном контексте. Начальное состояние оператора соответствует условию ложь. Оно таким и останется, если при очередном вычислении оператора левый операнд соответствует условию ложь, причем значение, вырабатываемое оператором диапазона, будет также соответствовать условию ложь. Если же при очередной проверке левый операнд обратился в истину, внутреннее состояние оператора диапазона станет истиной — соответственно возвращаемое оператором значение также будет истиной. Оператор будет оставаться в таком состоянии до тех пор, пока в истину не обратится правый операнд. Как только это произойдет, внутреннее состояние оператора немедленно обратится снова в ложь, однако вы это увидите только при следующем обращении к оператору — последнее выданное оператором диапазона значение будет истина. Очередное выполнение оператора диапазона приведет к проверке левого операнда, и весь цикл может повториться сначала.
Оператор диапазона проверяет только левый операнд, если внутреннее состояние оператора соответствует условию ложь, и только правый операнд, если его внутреннее состояние соответствует значению истина. При переключении внутренне го состояния оператора из истины в ложь проверка левого операнда откладывает ся до следующего обращения к оператору, а в качестве выходного значения оператора возвращается истина. При переключении же из состояния ложь в состояние истина оператор диапазона обычно тут же проверяет свой правый операнд. Если результат оказался истиной, внутреннее состояние переключается обратно в ложь, однако значение ложь опять-таки может быть выдано в качестве выходного значения только при следующем обращении к данному участку кода, а текущим выходным значением будет истина. Если же вы хотите при переключении из состояния ложь в состояние истина отложить проверку правого операнда до следующего обращения к оператору диапазона, используйте три точки ... вместо двух. (Первый вариант поведения характерен для awk, второй — для sed.)
Однако часто вместо правого и левого операндов оператора диапазона указываются отнюдь не логические условия, а числовые или строковые константы. Если левый или правый операнды оказываются константой, то вместо вычисления соответству ющего операнда («проверки условия») специальная переменная Perl $. (она хранит текущий номер считанной из файла строки для файла, к которому было последнее обращение) сравнивается с константой по признаку «больше или равно».
Такое странное поведение оператора диапазона в скалярном контексте связано с тем, что он моделирует операторы, которые в awk и sed проверяют номер строки, считанной из файла. Например, чтобы напечатать лишь часть строк из входного файла, используется команда:
while (<>)
{ if (101..200) {print;} # Печать строк 101-200 }
Чтобы понять работу оператора диапазона в скалярном контексте, рекомендует ся внимательно разобраться с несколькими образцами кода, написанного профессиональными программистами. Некоторые из примеров приведены в документа ции Perl, а также могут быть взяты из его библиотечных модулей.
Подсказка. В качестве условия ложь оператор диапазона возвращает пустую строку, в качестве условия истина — количество успешных обращений к данному оператору диапазона. При первом успешном обращении это число равно единице. Оно сбрасывается обратно в единицу в случае перехода к следующему оператору диапазона или при возникновении условия ложь. Чтобы особым образом обработать первый элемент, для которого оператор диапазона вернул значение истина, достаточно проверить, не равно ли это значение единице. Чтобы особым образом обработать последний элемент, можно использовать следующее свойство Perl: если оператор диапазона вызывается последний раз (то есть в момент перехода в состояние ложь), возвращаемое им значение-счетчик содержит суффикс «Е0» (значение, возвращаемое оператором диапазона, является строкой, а не числом, но, как уже отмечалось, Perl не делает большой разницы между числами и строками, преобразуя их из одного формата в другой по мере необходимости). Этот суффикс, характерный для числа с плавающей точкой, не влияет на числовое значение строки-счетчика, однако может служить индикатором, что достигнут последний элемент.
Тернарный условный оператор
Условный оператор ?: требует трех операндов. Его работа подобна конструкции if-then-else. Если операнд, указанный перед вопросительным знаком, соответству ет условию истина, то вычисляется и возвращается в качестве значения второй операнд, расположенный между вопросительным знаком и двоеточием. Если же значение первого операнда соответствует условию ложь, то вычисляется и возвращается третий операнд.
В следующем примере условный оператор вычисляет абсолютное значение числа, вводимого пользователем (предположим, что встроенной функции abs не существует):
while (<>)
{ print $_ >= 0 ? $_ : -$_ }
Вводимое число сравнивается с нулем. Если число неотрицательное (больше или равно нулю), то программа выводит число без изменений. В противном случае используется унарный минус, который инвертирует знак числа перед выводом.
В следующем примере числа из диапазона 0_15, вводимые пользователем, преобразуются в шестнадцатеричный формат:
while (<>) {
print $_ < 10 ? $_ :
"{\((a, b, c, d, e, f)[$_ - 10])}\n";
}
Здесь нет проверки на ошибки. Можно составить более аккуратную программу с вложенными операторами ?:, которая проверяет вводимое значение и выводит сообщение об ошибке, если введенное число не может быть преобразовано к шестнадцатеричному числу, состоящему из одной цифры:
Оператор-запятая
while (<>) {
print $_ > 0 && $_ < 10 ? $_ :
"{\($_ < 16 ? (a, b, c, d, e, f)[$_ - 10] :
\"Number is not a single hex digit.\"
)}\n";
}
(Чтобы разобраться, как работает этот пример, требуется материал, который рассматривается позднее.)
Оператор присвоения
Оператор присвоения заносит данные, указанные в качестве правого операнда, по месту, указанному левым операндом. В качестве левого операнда должны выступать так называемые «левые значения» (обычно это переменные, более подробно см. главу 2, раздел «Что такое "левое значение"?»), в качестве правого операнда — любое вычисляемое выражение:
$variable = 5;
Подобно языку С, в Perl можно использовать сокращенные формы оператора присваивания. Например, комбинирование присвоения с умножением записывается следующим образом:
$doubleme *= 2;
В этом примере содержимое переменной $doubleme умножается на два, а результат помещается в ту же самую переменную. Список разрешенных «коротких» операторов присвоения выглядит как
**= += *= &= <<= &&=\ -= /= |= >>= ||= .= %= ^= x=
В отличие от С в Perl оператор присвоения представляет собой полноценное «левое значение», которое в случае необходимости можно использовать как переменную. Например, в следующем случае мы отсекаем последний символ именно от переменной $input, а не от выражения, возвращаемого оператором присваивания:
chop($input = 123);
print $input;
12
Это свойство помогает создавать немного более компактный код. Например, следующий фрагмент программы считывает строку из входного потока, удаляет последний символ (конец строки) и оставляет результат в переменной $input — и все в одну строчку:
chop($input = <>);
Оператор-запятая
Этот оператор работает различным образом в скалярном и списковом контексте. В скалярном контексте он вычисляет свой левый аргумент, отбрасывает получен
ный результат, затем вычисляет правый аргумент и возвращает его в качестве значения. Например:
$variable = (3, 4, 5);
print $variable;
5
В списковом контексте оператор-запятая выступает в качестве разделителя списка, помещая в список оба аргумента, как в следующем примере:
@array = (3, 4, 5);
print join(", ", @array);
3, 4, 5
Конструкция => является синонимом для оператора-запятой. Начиная с версии Perl 5.001 этот оператор заставляет интерпретировать аргумент, указанный слева, как текстовую строку.
Подсказка. Символ => является диграфом. Диграф — это символ, составленный из двух идущих подряд литер.
Списки, стоящие слева
Оператор списка (набор элементов, разделенных запятой) имеет различный приоритет, когда сравнивается с операторами, стоящими слева и справа. Относитель но операторов, стоящих справа, приоритет у оператора-списка меньше, чем для операторов, стоящих слева. Например, тот факт, что оператор-запятая на одну ступень более приоритетен, чем правый оператор списка, позволяет завершить список до того, как подать его на вход предшествующего списочного оператора. Вот пример, уже использовавшийся ранее:
print 1, 2, 3, 4, sort 9, 8, 7, 6, 5;
123456789
Рассмотрим, например, запятую, стоящую после числа 7. Список из трех чисел 9, 8 и 7, стоящий слева от нее, имеет более слабый приоритет, чем запятая, и поэтому вместо того, чтобы немедленно «скормить» имеющийся список оператору sort, будет продолжено пополнение списка новым элементом 6.
Логическое NOT
Оператор not возвращает логическое отрицание своего операнда. Его действие аналогично оператору !, рассмотренному ранее, за тем исключением, что приоритет этого оператора значительно ниже (таким образом, можно не беспокоиться о круглых скобках в выражениях, разделенных этим оператором). Вот пример:
print ".", (not 0), "\n";
print ".", (not 1), "\n";
.1.
..
(Здесь в качестве логического значения ложь выводится пустая строка.)
Логическое XOR
Логическое AND
Оператор and работает аналогично &&, но имеет более низкий приоритет. Он сперва вычисляет левый операнд, а его правый операнд вычисляется только в том случае, когда соответствует условию истина. Другими словами, оператор and работает по сокращенной схеме, как и оператор &&. (В разделе, посвященном оператору or, показано, как это можно использовать.)
Логическое OR
Оператор or работает аналогично ||, но имеет более низкий приоритет. Низкий приоритет оператора гарантирует, что его можно использовать при вызовах списковых операторов без необходимости заключать списки в круглые скобки.
Оператор or сперва вычисляет левый операнд, а правый операнд вычисляется только в том случае, если левый соответствует условию ложь. Другими словами, or работает по такой же сокращенной схеме, что и оператор ||.
Если хотя бы один из операндов соответствует условию истина, то оператор or возвращает значение первого такого операнда в качестве результата (поскольку в Perl любое ненулевое и непустое значение рассматривается как истина, возможна масса вариантов). Это можно использовать для сокращения числа условных операторов. Например, в следующем фрагменте кода делается попытка открыть файл, а в случае неудачи выводится сообщение об ошибке и работа программы прекращается с помощью функции die:
open FileHandle, $filename or die "Cannot open $filename\n";
(Так как приоритет оператора or меньше, чем у операторов open и die, скобки не нужны.)
Логическое XOR
Оператор xor возвращает логическое «исключающее или» для двух окружающих его операндов. Работа xor аналогична работе оператора ^, но его приоритет существенно ниже.