Тарифы Услуги Сим-карты

Основы Perl — условные операторы и циклы. Сравнения и "умное" сопоставление

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

Все операции PERLа подразделяются на:

Операции ввода-вывода описаны в статье "Perl: описатели файлов ", операции с регулярными выражениями — "Perl: регулярные выражения ", раздел "Операции с регулярными выражениями".

Операции сравнения

Операции сравнения сравнивают два операнда и возвращают 1 или 0 в зависимости от результата этого сравнения. PERL содержит два набора операций сравнения: для сравнения чисел и для сравнения строк. Операции первой группы сначала преобразуют операнды в числа, а затем их сравнивают. Операции второй группы преобразуют операнды в строки и сравнивают их в лексикографическом порядке . Если задана директива use locale , то сравнение срок производится с учетом национальной настройки.

Сравнение чисел Сравнение строк Название Описание
a < b a lt b Меньше 1, если левый операнд меньше, чем правый операнд.
a > b a gt b Больше 1, если левый операнд больше, чем правый операнд.
a <= b a le b Не больше 1, если левый операнд меньше или равен правому операнду.
a => b a ge b Не меньше 1, если левый операнд больше или равен правому операнду.
a == b a eq b Равно 1, если левый операнд равен правому операнду.
a != b a ne b Не равно 1, если левый операнд не равен правому операнду.
a <=> b a cmp b Сравнение -1, если левый операнд меньше, чем правый; 0, если они равны; 1, если левый операнд больше, чем правый.

Следующий пример

$a = 299;
$b = 30;
if ($a < $b)
{
print "Число $a меньше числа $b\n";
}
if ($a lt $b)
{
print "Строка $a меньше строки $b\n";
}

выведет сообщение: Строка 299 меньше строки 30 .

Логические операции

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

Операции and , or , xor и not имеют самый низкий приоритет среди всех операций PERLа. Это позволяет использовать их в правой части операторов без дополнительных круглых скобок, например:

Open HANDLE, "file.dat" or die "Error: $!\n";

Использование операции || в этом примере потребует скобок:

Open(HANDLE, "file.dat") || die "Error: $!\n";

Битовые операции

Битовые операции применяются к операндам, представленным как двоичные числа (т. е. как цепочки битов), и возвращают значение, означающее результат операции.

Операция Название Описание
a & b Побитовое AND Возвращает в каждой битовой позиции 1, если соответствующие позиции обоих операндов равны 1.
a | b Побитовое OR Возвращает в каждой битовой позиции 1, если соответствующая позиция хотя бы одного операнда равна 1.
a ^ b Побитовое XOR Возвращает в каждой битовой позиции 1, если соответствующая позиция ровно одного операнда равна 1.
~a Побитовое NOT Унарная операция. Инвертирует каждую битовую позицию операнда.
a << b Сдвиг влево Сдвигает двоичное представление левого операнда влево на количество бит, заданное вторым операндом.
a >> b Cдвиг вправо Сдвигает двоичное представление левого операнда вправо на количество бит, заданное вторым операндом.

Операции сдвига применимы только к числовым операндам, но остальные битовые операции применимы и к числам, и к строкам. Если одним из операндов битовой операции является число, то оба операнда преобразуются в целые числа. Если же оба операнда строки, то они преобразуются в строки одинаковой длины и операция выполняется над каждым битом полученных строк. Это позволяет нам применять битовые операции к операндам любой длины. При этом операции | и ^ дополняют более короткую строку справа нулевыми битами, а операция & обрезает более длинную строку до размера более короткой. Мы можем явно указать тип операции, используя кавычки для строк или 0+ для чисел. Примеры:

Print 1.9 | 2; # 3 (плавающий операнд урезан до целого)
print 150 | "105"; # 255 (0x96 | 0x69 равно 0xFF)
print "150" | "105"; # "155" (в ASCII)
$a = 150;
$b = "105";
print 0+$a | 0+$b; # числовые операнды
print "$a" | "$b"; # строковые операнды

На выполнение битовых операций влияют директивы управления арифметикой. По умолчанию, целые числа считаются беззнаковыми, поэтому операция правого сдвига заполняет старшие биты результата нулями, а операция ~0 возвращает наибольшее положительное целое число. Директива use integer приводит к тому, что целые числа начинают интерпретироваться как числа со знаком. Поэтому в зоне ее действия операция правого сдвига заполняет старшие биты результата знаковым битом числа, а операция ~0 возвращает число -1 .

Унарные операции

Унарный плюс

Унарный плюс (+) возвращает свой операнд. Единственное осмысленное применение этой операции состоит в том, чтобы отделять имя функции от ее аргументов в скобках. Рассмотрим такой пример:

Print (10 + 20) / 10;

Вопреки ожиданиям, этот оператор печатает не число 3, а число 30. Дело в том, что круглые скобки вокруг аргументов функции имеют в PERLе наивысший приоритет, т. е. операция print (10 + 20) выполняется раньше, чем деление на 10. Желаемый результат достигается использованием унарного плюса перед скобками, который понижает их приоритет:

Print +(10 + 20) / 10;

Унарный минус

Унарный минус (-) изменяет знак числового операнда на противоположный. Если же операнд является строкой, то возвращается следующий результат:

  • если строка начинается с символа "+" или "-", то в результате этот символ заменяется на противоположный;
  • в остальных случаях возвращается конкатенация символа "-" и исходной строки.

В частности, конструкции -слово и "-слово" эквивалентны.

Создание ссылки

Инкремент и декремент

Операции ++ и -- называются соответственно операциями инкремента и декремента . Инкремент увеличивает значение числовой переменной на 1. Если она используется как префикс (++a), то возвращает значение операнда после увеличения его на 1. Если же она используется как постфикс (a++), то возвращает значение операнда перед увеличением его на 1. Если операнд инкремента является строковой переменной, соответствующей образцу /^**$/ , то выполняется посимвольный инкремент строки с переносом. Примеры:

Print ++($x = "99"); # "100"
print ++($x = "a0"); # "a1"
print ++($x = "Az"); # "Ba"
print ++($x = "zz"); # "aaa"

Декремент применяется только к числовым переменным и уменьшает значение переменной на 1. Если эта операция используется как префикс (--a), то она возвращает значение операнда после уменьшения его на 1. Если она используется как постфикс (a--), то возвращает значение операнда перед уменьшением его на 1. Примеры:

Print --($x = 99); # 98
print (($x = 99)--); # 99
print --($x = "a0"); # -1

Именованные унарные операции

Аддитивные операции

Существуют две аддитивные операции над числами (сложение и вычитание) и одна над строками (конкатенация).

Print "1" + "2"; # 3
print "1" . "2"; # "12"

Мультипликативные операции

Существуют четыре мультипликативные операции над числами (умножение, деление, остаток по модулю и возведение в степень) и одна над строками (повторение).

Операция Название Описание
a * b Умножение Возвращает произведение двух числовых операндов.
a / b Деление Возвращает частное от деления двух числовых операндов.
a % b Остаток по модулю Возвращает целый остаток от деления левого операнда на правый. Плавающие числа перед операцией округляются до целых. Если b отрицательно, то и результат отрицателен.
a ** b Возведение в степень Возвращает a в степени b .
a x b Повторение Левый операнд должен быть строкой или списком, правый — числом. Возвращает операнд a , повторенный b раз.

Print "-" x 80; # печатает строку минусов
@ones = (1) x 5; # @ones = (1, 1, 1, 1, 1)
@ones = (2) x @ones; # @ones = (2, 2, 2, 2, 2)

Операции присваивания

Операции присваивания присваивают левому операнду результат операции, который определяется правым операндом и самой операцией. Базовая операция присваивания имеет вид a = b , что означает: присвоить переменной a значение операнда b . Все остальные операции присваивания являются сокращениями других операций, совмещенных с присваиванием, как показано в следующей таблице.

Операция Значение
a += b a = a + b
a -= b a = a - b
a .= b a = a . b
a *= b a = a * b
a /= b a = a / b
a %= b a = a % b
a x= b a = a x b
a **= b a = a ** b
a <<= b a = a << b
a >>= b a = a >> b
a &= b a = a & b
a |= b a = a | b
a ^= b a = a ^ b
a &&= b a = a && b
a ||= b a = a || b

Операция присваивания возвращает свой левый операнд и потому может использоваться в левой части следующей операции присваивания. Например

($a += 2) *= 3;

эквивалентно

$a += 2;
$a *= 3;

Присваивание списка списку возвращает список переменных, которым присвоены значения. Присваивание списка скаляру возвращает количество элементов в этом списке.

q-операции

В большинстве языков программирования кавычки служат для образования строковых констант. Однако, в PERLе они являются операциями, обеспечивающими интерполяцию строк и сопоставление с образцом. Такие операции мы будем называть q-операциями (от английского слова quote, т. е. кавычка). PERL содержит два набора q-операций, перечисленных в следующей таблице.

Кавычки q-операция Значение Интерполяция
"" q() Константа Нет
"" qq() Константа Да
`` qx() Команда Да (если ограничители не "")
qw() Список слов Нет
qr() Регулярное выражение Да (если ограничители не "")
// m() Сопоставление с образцом Да (если ограничители не "")
s()() Подстановка Да (если ограничители не "")
tr()() Транслитерация Только escape-последовательностей

Здесь вместо скобок () можно использовать любой символ, не являющийся буквой, цифрой или пробелом, либо парные ограничители: (), , {}, <>. Примеры использования q-конструкций для создания строковых констант были приведены в п. 6.2.3.2 ; об их использовании в регулярных выражениях см. п. 6.4.6 .

Прочие операции

Разадресация

Операция разадресации имеет вид:

Операции связывания

Большинство операций над регулярными выражениями работают со специальной переменной $_ . Для сопоставления с образцом произвольных строк используются операции связывания , которые имеют вид:

A =~ ba !~ b

где a — любое строковое выражение, а b — образец для сопоставления, подстановки или транслитерации. Операция =~ возвращает истину, если сопоставление, подстановка или транслитерация прошли успешно, и ложь в противном случае. Операция!~ возвращает логическое отрицание результата =~ . Примеры использования этих операций см. в п. 6.4.6 .

Задание диапазона

Операция задания диапазона имеет вид:

На самом деле это две разные операции в зависимости от контекста. В контексте списка эта операция возвращает список значений (a a+1 … b) . Для строк диапазон формируется с использованием инкремента , например:

Foreach (1..10)
{
# выполняет цикл 10 раз
}
@a = @a[$#a-4 .. $#a]; # вырезает последние пять элементов
@a = ("abc".."abe"); # @a = ("abc", "abd", "abe")

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

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

В остальном операции.. и... идентичны. Правый операнд не оценивается, пока операция находится в состоянии "ложь", а левый операнд не оценивается, пока операция находится в состоянии "истина". Значение, возвращаемое данной операцией, это пустая строка в ложном состоянии, и последовательные целые числа, начиная с 1, в истинном состоянии. Последовательный номер сбрасывается для нового диапазона. К последнему номеру диапазона добавляется строка "E0". Если какой-либо из операндов является константой, то он неявно сравнивается со специальной переменной $. (содержащей номер текущей строки текущего файла). Следующий пример открывает заданный файл и печатает его строки с первой по пятую.

Open HANDLE, "filename.ext";
while ()
{
print if (1..5);
}

Условная операция

Условная операция — это тернарная операция, которая имеет вид:

Test ? value1: value2

где test , value1 и value2 — любые выражения. Если test истинно, то операция возвращает значение value1 , в противном случае она возвращает значение value2 . Пример:

Printf "У меня %d дру%s.\n", $n, ($n == 1) ? "г" : (($n < 5) ? "га" : "зей");

Эта операция может использоваться в левой части операции присваивания, если и value1 , и value2 являются переменными, например:

($test ? $a: $b) = $c;

Операция запятая

Операция запятая вызывает последовательное вычисление значений двух выражений и возвращает второе из них. Она имеет вид:

Expr1, expr2

где expr1 и expr2 — любые выражения. В списках запятая служит разделителем элементов списка.

PERL содержит синоним этой операции вида

Expr1 => expr2

Такая форма обычно используется для парных элементов списка, например, при инициализации ассоциативных массивов. Кроме того, если левый операнд операции => является словом, то он интерпретируется как строка (см. п. 6.2.7 )

Порядок выполнения операций

При вычислении выражений PERL учитывает приоритет операций : операции, имеющие больший приоритет, выполняются первыми. Для изменения порядка выполнения операций следует пользоваться круглыми скобками: выражение, заключенное в них, имеет наивысший приоритет. В следующей таблице приведен список всех операций языка PERL в порядке убывания их приоритетов; операции, расположенные в одной строке таблицы, имеют равный приоритет и выполняются либо слева направо, либо справа налево, как указано в таблице.

Операция Ассоциативность
термы и списки левая
-> левая
++ -- нет
** правая
! ~ \ + (унарный) - (унарный) правая
=~ !~ левая
* / % x левая
+ - . левая
<< >> левая
именованные унарные операции нет
< > <= >= lt gt le ge нет
== != <=> eq ne cmp нет
& левая
| ^ левая
&& левая
|| левая
.. ... нет
?: правая
= op = правая
, => левая
not правая
and левая
or xor левая

В этой части речь пойдет об условных операторах и циклах.

Условные операторы

Как всегда, начнем сразу с примеров.

$a = shift ;
if ($a > 10 ) {
print "a > 10\n " ;
}

Программистам на C-подобных языках эта конструкция должна быть до боли знакома, так что комментировать тут особо нечего. Скажу лишь, что в отличие от Си, опустить фигурные скобки тут нельзя. Точнее говоря, способ есть, но о нем чуть ниже. Конструкции if-else и if-else-if-… на языке Perl выглядят следующим образом:

$a = shift ;
if ($a > 10 ) {
print "a > 10\n " ;
} else {
print "a <= 10\n " ;
}

if ($a > 0 ) {
# do something
} elsif ($a == 0 ) {
# do something
} else {
# do something else
}

В общем, все все как мы и ожидаем за одним исключением. Никакого «else if» в Perl нет — вместо этого следует использовать elsif и только его. Elsif может повторяться несколько раз, else использовать не обязательно, фигурные скобки опустить нельзя.

В отличие от других языков программирования, в Perl также предусмотрен оператор unless. Следующие два куска кода делают одно и то же:

unless ($a == 0 ) {
# "... если только a не равно нулю"
...
}

if ($a != 0 ) {
# то же самое
# ...
}

Unless можно использовать в сочетании с elsif и else, однако никакого «elsunless» в Perl нет.

В Perl есть возможность уменьшить объем кода, если в случае выполнения условия должна быть выполнена только одна строка кода. Следующие примеры делают одно и то же:


if ($a > $b ) {
exit 1 ;
}


unless ($b == $c ) {
exit 2 ;
}

# если условие истинное - завершить скрипт с кодом 1
exit 1 if ($a > $b ) ;
# если b == c, продолжить выполнение скрипта
exit 2 unless ($b == $c ) ;

При этом скобки в последнем примере можно не использовать:

# если условие истинное - завершить скрипт с кодом 1
exit 1 if $a > $b ;

Если вы пишите на Java/PHP или другом Си-подобном языке, такая конструкция скорее всего будет для вас непривычной, но на практике она действительно удобна. В русском языке мы ведь тоже обычно говорим «завершить программу, если …», а не «если …, то …».

Также, как в C/C++ и PHP, в Perl имеется тернарный оператор?: . Работает он аналогично конструкции if-else, только внутри выражений:

if ($a > $b ) {
$a = $a / $b ;
} else {
$a = $b / $a ;
}

# аналогичный код, использующий оператор "знак вопроса"
# одна строчка кода вместо пяти
$a = $a > $b ? $a / $b: $b / $a ;

Также хотелось бы сказать пару слов об операторах сравнения. Операторы сравнения в языке Perl делятся на две группы — производящие сравнение чисел и сравнение строк.

Как вы помните, скаляры в Perl можно интерпретировать либо как строки, либо как числа. Например, число 123 больше числа 45, однако строка «123» меньше строки «45». Вот для чего потребовалось несколько групп операторов сравнения. Сравнение строк в Perl производится таким же образом, как и в других современных языках программирования, потому я надеюсь, что тут вопросов не возникает.

Циклы for, foreach, while/until, do..while/until

Цикл for прекрасно знаком всем программистам:

# вывести строку "0 1 2 3 4"
for ($i = 0 ; $i < 5 ; $i ++ ) {
print "$i " ;
}

В круглых скобках через точку с запятой записывается:

  1. Код, выполняемый перед началом цикла.
  2. Условие, проверяемое перед началом (а не в конце, как думают многие) каждой итерации. Если оно ложно, выполнение цикла завершается.
  3. Код, выполняемый после каждой итерации.

Как и в случае с условными операторами, в циклах опускать фигурные скобки нельзя (для этого также есть специальная форма записи — см ниже).

Цикл foreach должен быть хорошо знаком программистам на PHP:

@arr = (0 , 1 , 2 , 3 , 4 ) ;
# вывести строку "0 1 2 3 4"
foreach $i (@arr ) {
print "$i " ;
}

Тело цикла foreach выполняется для каждого элемента массива, указанного в скобках. Важная особенность foreach — в переменную $i не копируется элемент массива @arr, как думают многие . Переменная $i в теле цикла — это и есть сам элемент массива . Таким образом, следующий код увеличивает на единицу значение каждого элемента массива @arr:

$i = 19880508 ;
foreach $i (@arr ) {
$i ++;
}
# $i по прежнему равен 19880508

Также foreach можно использовать для работы с хэшами:

%hash = (
aaa => 1 ,
bbb => 2 ,
) ;
# функция keys возвращает массив, содержащий все ключи хэша
foreach $k (keys %hash ) {
print "$k => $hash{$k}\n " ;
}

В Perl цикл foreach имеет короткую форму:

# если Вы забыли, что делает оператор qw,
# вернитесь к первой части "основ программирования на Perl"
for $i (qw/1 2 3/ ) {
print "$i " ;
}

То есть фактически везде вместо foreach можно писать просто for. Perl не перепутает такую запись с настоящим циклом for, потому что в последнем нужно писать точки с запятой и так далее.

Циклы while, until и do работают точно так же, как и в C++ или Pascal/Delphi:

# выводим "1 2 3 4 5" четырьмя разными способами

$i = 0 ;
while ($i < 5 ) { # пока $i меньше пяти
print ++ $i . " " ;
}
print "\n " ; # новая строка

$i = 0 ;
until ($i == 5 ) { # пока $i не станет равно пяти
print ++ $i . " " ;
}
print "\n " ;

$i = 0 ;
do {
print ++ $i . " " ;
} while ($i < 5 ) ; # проверка в конце цикла
print "\n " ;

$i = 0 ;
do {
print ++ $i . " " ;
} until ($i == 5 ) ;
print "\n " ;

По аналогии с операторами if и unless, существует сокращенная форма записи циклов:

$i = 0 ;
print ++ $i . " " while ($i < 5 ) ;
print "\n " ;

$i = 0 ;
print ++ $i . " " until ($i == 5 ) ;
print "\n " ;

print "$_ " for (qw/1 2 3 4 5/ ) ; # можно и foreach(qw/1 2 3 4 5/);
print "\n " ;

Обратите внимание на последний цикл foreach. Мы ведь помним, что это сокращенная запись foreach, а не цикл for, верно? Последний кстати не имеет короткой записи. Так вот, здесь не было указано имя переменной, с помощью которой мы будем обращаться к элементам массива. В сокращенной записи foreach ее нельзя использовать. В этом случае используется специальная переменная — $_. Следующий пример также вполне рабочий:

for (qw/1 2 3 4/ ) {
print "$_" ;
}

Его, кстати, можно переписать следующим образом.

Есть несколько способов сравнения объектов в Perl. Можно проверить равенство значений используя инфиксный оператор === . Для неизменных (immutable ) объектов (значения которых нельзя изменить, литералов. Например литерал 7 всегда будет 7) это обычное сравнение значений. Например "hello"==="hello" всегда верно потому, что обе строки неизменны и имеют одинаковое значение.

Для изменяемых объектов === сравнивает их идентичность. === возвращает истину, если его аргументы являются псевдонимами одного и того же объекта. Или же двое объектов идентичны, если это один и тот же объект. Даже если оба массива @a и @b содержат одинаковые значения, если их контейнеры разные объекты, они будут иметь различные идентичности и не будут тождественны при сравнении === :

My @a = 1, 2, 3; my @b = 1, 2, 3; say @a === @a; # 1 say @a === @b; # 0 # здесь используется идентичность say 3 === 3; # 1 say "a" === "a"; # 1 my $a = "a"; say $a === "a"; # 1

Оператор eqv возвращает Истина если два объекта одного типа и одинаковой структуры. Так для @a и @b указанных в примере, @a eqv @b истинно потому, что @a и @b содержат одни и те же значения. С другой стороны "2" eqv 2 вернет False , так как аргумент слева строка, а справа - число, и таким образом они разных типов.

Так же как == преобразует свои аргументы в числа, инфиксный оператор eq сравнивает равенство строк, преобразуя аргументы в строки при необходимости.

If $greeting eq "hello" { say "welcome"; }

Другие операторы сравнивают строки лексикографически.

На самом деле более удобная форма для!== , который в свою очередб представляет собой объединеие метаоператора! и инфиксного оператора == . Такое же обяснение приминительно к ne и!eq .

Операторы three-way сравнения получают два операнда и возвращают Order::Increase , если операнд слева меньше, Order::Same - если равны, Order::Decrease - если операнд справа меньше (Order::Increase , Order::Same и Order::Decrease являются перечислениями (enums) ; см.). Для числовых сравнений используется оператор <=> , а для строковых это leg (от англ. l esser, e qual, g reater). Инфиксный оператор cmp также является оператором сравнения, возвращающий три результата сравнения. Его особенность в том, что он зависит от типа аргументов: числа сравнивает как <=> , строки как leg и (например) пары сначала сравнивая ключи, а затем значения (если ключи равны).

Say 10 <=> 5; # +1 say 10 leg 5; # because "1" lt "5" say "ab" leg "a"; # +1, lexicographic comparison

Типичным применением упомянутых three-way операторов сравнения является сортировка. Метод.sort в списках получает блок или функцию, которые сравнивают свои два аргумента и возвращают значения отрицательные если меньше, 0 - если аргументы равны и больше 0, если первый аргумент больше второго. Эти результаты затем используются при сортировке для формирования результата.

Say ~.sort; # output: Concrete abstract say ~.sort: -> $a, $b { uc($a) leg uc($b) }; # output: abstract Concrete

По умолчанию используется сортировка чувствительная к регистру, т.е. символы в верхнем регистре "больше" символов в нижем. В примере используется сортировка без учета регистра.

Разные операторы сравнения приводит свои аргументы к определённым типам перед сравнением их. Это полезно, когда требуется конкретное сравнение, но типы параметров неизвестны. Perl 6 предоставляет особый оператор который позволяет производить сравнение "Делай Как Надо" (Do The Right Thing) с помощью ~~ - оператора "умного" сравнения.

If $pints-drunk ~~ 8 { say "Go home, you"ve had enough!"; } if $country ~~ "Sweden" { say "Meatballs with lingonberries and potato moose, please." } unless $group-size ~~ 2..4 { say "You must have between 2 and 4 people to book this tour."; }

Оператор "умного" сопоставления всегда решает какого рода сравнение производить в зависимости от типа значения в правой части. В предыдущих примерах эти сравнения были числовым, строковым и сравнением диапазонов соответственно. В данной главе была продемонстрирована работа операторов сравнения: чисел - == и строк eq .Однако нет оператора для сравнения диапазонов. Это является частью возможностей "умного" сопоставления: более сложные типы позволяют реализовывать необычные идеи сочетая сравнения их с другими.

"Умное" сопоставление работает, вызывая метод ACCEPTS у правого операнда и передавая ему операнд слева как аргумент. Выражение $answer ~~ 42 сводится к вызову 42.ACCEPTS($answer) . Данная информация пригодится, когда вы прочитаете последующие главы, посвященные классам и методам. Вы тоже напишите вещи, которые смогут производить "умное" сопоставление, реализовав метод ACCEPTS для того, чтобы "работало как надо".

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

Таблица 4.1. Операции отношения

Операция Числовая Строковая Значение
Равенство == eq Истина, если операнды равны, иначе ложь
Неравенство != ne Истина, если операнды не равны, иначе ложь
Меньше < lt Истина, если левый операнд меньше правого, иначе ложь
Больше > gt Истина, если левый операнд больше правого, иначе ложь
Меньше или равно <= le Истина, если левый операнд больше правого или равен ему, иначе ложь
Больше или равно >= ge Истина, если правый операнд больше левого или равен ему, иначе ложь
Сравнение <=> cmt 0, если операнды равны
1, если левый операнд больше правого
-1, если правый операнд больше левого

Результатом операций отношения (кроме последней сравнения) является Истина, значение 1, или Ложь, пустая строка "".

Замечание
Значение истина в арифметических операциях интерпретируется как число 1, а в строковых как строка "1". Значение ложь в арифметических операциях интерпретируется как число 0, а в строковых как пустая строка " ".

Числовые операции отношения

Числовые операции отношения применяются к числовым данным, причем один или оба операнда могут задаваться строкой, содержащей правильное десятичное число. Если в числовых операциях отношения какой-либо из операндов задан строкой, содержимое которой не представляет правильное десятичное число, то его значение принимается равным о и отображается предупреждение о некорректном использовании операнда в числовой операции отношения (если включен режим отображения предупреждений интерпретатора Perl). Смысл операций отношения для числовых данных соответствует обычным математическим операциям сравнения чисел (пример 4.7).

123 > 89; # Результат: 1 (истина)

123 < 89; # Результат: "" (ложь)

123 <= 89; # Результат: "" (ложь)

89 <= 89; # Результат: 1 (истина)

23 >= 89; # Результат: "" (ложь)

23 <=> 89; # Результат: -1 (правый операнд больше левого)

89 <=> 23; # Результат: 1 (правый операнд больше левого)

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

#! peri -w
$z = 0.7;

$zz = 10+0.7-10; # Переменная $zz содержит число 0.7

# Печать строки "z равно zz", если равны значения переменных $z и $zz print "z равно zz\n" if ($z == $zz);

При попытке выполнить пример 4.8 мы с удивлением обнаружим, что наша программа ничего не напечатает. В чем же дело? Разгадка лежит в операторе вычисления значения переменной $zz. При выполнении арифметических операций в результате ошибок округления получается значение 0.699999999999999 (можете вставить оператор печати переменной $zz и убедиться в этом), хотя и близкое к 0.7, но не равное ему в точности. Следовательно, операция сравнения отработала верно!

Совет
Не используйте операцию сравнения на равенство вещественных чисел, ее результат может не соответствовать ожидаемому с точки зрения математики. Если необходимо проверить равенство двух вещественных чисел, то лучше сравнивать абсолютное значение их разности с некоторым очень маленьким числом (в зависимости от требуемой точности):

abs($a-$b) <= 0.00000001; # Проверка равенства

Строковые операции отношения

Сравнение строковых данных базируется на их упорядочении в соответствии с таблицей кодов ASCII, т. е. символ с меньшим кодом ASCII предшествует символу с большим кодом. Сравнение строк осуществляется посимвольно слева направо. Это означает, что если равны первые символы строк, то сравниваются вторые и если они равны, то сравниваются третьи и т. д. Причем, если строки разной длины, то в конец строки меньшей длины добавляется недостающее для равенства количество символов с кодом о. Следует отметить, что в отличие от некоторых других языков программирования в Perl замыкающие строку пробельные символы являются значимыми при сравнении строк. В примере 4.9 показаны сравнения строк, иллюстрирующие изложенные правила.

"A" It "a"; # Результат: истина (код "А" - \101, код "а" - \141)
"a" It "aa";
# с кодом \000, который меньше кода \141
# второго символа "а" строки правого операнда)
"a" It "a "; # Результат: истина (к строке "а" добавляется символ
# с кодом \000, который меньше кода \040
# замыкающего пробела строки правого операнда)
"12" It "9"; # Результат: истина (код "1" - \061, код "9" - \071)
"9" eq 09"; # Результат: ложь (код " " - \040, код "О" - \060)

Обратим внимание на две последние операции сравнения строковых литералов. Содержимое их операндов может быть преобразовано в правильные числа, и поэтому к ним применимы аналогичные числовые операции отношения. Однако их результат будет существенно отличаться от результата выполнения строковых операций отношения. При использовании операции < в предпоследнем выражении результат будет Ложь, а если в последнем выражении применить операцию ==, то результат будет Истина. Об этом всегда следует помнить, так как Perl автоматически преобразует символьные данные в числовые там, где это необходимо.