Perl условия. Сравнение скаляров в Perl. Условный оператор if

Рассмотрены интересные неочевидные конструкции Perl.

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

Также, некоторые вещи можно использовать не совсем стандартным образом для получения желаемого результата. Такие действия иногда называются «забивать гвозди микроскопом». Именно этим мы и будем заниматься.

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

Оператор «Венера»

0+ +0

Название

Свое название оператор получил от внешней схожести с символом Венеры.

Что делает?

Приводит аргумент слева, или справа, в зависимости от версии, к числовому виду. Например:

Print 0+ "23a"; print 0+ "3.00"; print 0+ "1.2e3"; print 0+ "42 EUR"; print 0+ "two cents";

Результат:

23 3 1200 42 0

Этот оператор с натяжкой можно использовать у себя в проектах, но не следует забывать о том, что с точки зрения Perl числами, например, являются: 0 but true 0E0 и некоторые еще хитрые константы. Еще следует отметить, что 0+ это метод, используемый для числового преобразования по умолчанию при использовании overload .

«Черепашка», «Детская коляска» или «Тележка из супермаркета»

@{}

Название

Свое название оператор получил из-за внешней схожести с черепахой или коляской. Примечателен тем, что был открыт Ларри Уоллом в 1994 году.

Что делает?

Это так называемый контейнерный оператор, который позволяет производить интерполяцию массива внутри строки. Элементы массива в результате будут разделены содержимым переменной $" .

Как работает?

Сначала содержимое принудительно вычисляется в списковом контексте, затем, незамедлительно, проводится разыменование (@{ }).

Print "Test i am @{[ die()]}"; print "here";

Результат работы:

Died at demo.pl line 1.

Этот оператор можно использовать для выполнения произвольного кода в строках, когда происходит интерполяция. Например, с его помощью можно сделать некоторые вещи проще, например, построение SQL-запросов:

My $sql = "SELECT id, name, salary FROM employee WHERE id IN (@{[ keys %employee ]}) SQL ";

но выигрыш в пару строк кода не оправдывает потенциальные проблемы в безопасности (хорошая практика в SQL - использование т.н. bind variables, которые будут подставлены при prepare). В виду неочевидности, использовать оператор в production не стоит, потенциальная уязвимость и уменьшение читабельности.

Bang Bang

!!

Данный оператор использовался еще тогда, когда Perl не было, его часто использовали программисты на С.

Как работает?

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

My $true = !! "a string"; my $false = !! undef;

В результате $true содержит 1 , а $false пустую строку "" . Данный оператор можно использовать для проверки, есть ли значение, например, в ссылке на хеш, например

!! $hash->{value} or die("Missing value");

Червяк

~~

Название?

Просто и незатейливо похож на гусеницу-пяденицу, и на многих червей также.

Как работает?

Оператор ~~ внешне очень похож на smart matching, но им не является потому, что это унарный оператор.

Унарная операция - это операция над одним операндом

Тогда как smart matching - операция бинарная (операция над двумя операндами, сложение, например).

Что делает?

Всего-лишь сокращенное на четыре (!) символа ключевое слово scalar .

Perl -Esay~~localtime Tue Jul 30 17:43:16 2013

Принцип действия оператора схож с оператором Bang Bang (!!), но отличается тем, что в Perl оператор ~ операторозависимый. Более подробно можно узнать, посмотрев в документации про побитовые операции в Perl. Применять этот оператор можно, но следует быть осторожным, неизвестно, как он будет себя вести на всех версиях perl.

Червяк-на-палочке

-~ ~-

Это высокоприоритетный оператор инкремента/декремента. -~ инкрементирует только числа, которые меньше нуля, а ~- декрементирует числа больше нуля

Приоритет этого оператора выше арифметических операторов, кроме возведения в степень (**). Например:

$y = ~-$x * 4;

Будет исполняться идентично:

$y = ($x-1)*4;

Но не как:

$y = ($x * 4) -1

Для того, чтобы данные операторы работали с беззнаковыми типами данных, необходимо использовать прагму integer (use integer). Этот оператор работает весьма неплохо и его можно применять в production, но делать этого не стоит, т.к. на нестандартных архитектурах его поведение может отличаться от вышеуказанного. Хотелось бы попробовать его в действии на ARM-платформах, но автор статьи не располагает подобным устройством.

Космическая станция

-+-

Открыт Alistair McGlinchy в 2005 году.

Этот оператор производит высокоприоритетное приведение к числовому виду, по поведению он похож на оператор «символ Венеры», но он отличается следующими вещами. Оператор Венеры (0+ или +0) использует бинарный оператор + , тогда как «Космическая станция» использует сконкатенированные три унарных оператора, а потому имеет более высокий приоритет.

Также, стоит помнить, что этот оператор имеет меньший приоритет, чем сконкатенированные операторы * и x . Принципы его работы можно проиллюстрировать следующим примером, в котором мы попробуем распечатать приведенное к числовому виду "20GBP" три раза:

Неправильно, т.к. возвращает преобразованный вариант от строки "20GBP20GBP20GBP" :

Print 0+ "20GBP" x 3; # 20

Неправильно, т.к. эквивалентно (print "20") x 3:

Print(0+ "20GBP") x 3; # 20

Правильно, но сильно длинно, сильно «лиспово»:

Print((0 + "20GBP") x 3); # 202020

Правильно - используя оператор «Космическая станция»:

Print -+- "20GBP" x 3; # 202020

Однако, т.к. унарный минус - и унарный + просто заменяет результат строки на его значение, то данный оператор не будет работать со следующего вида строками:

  • С теми, что начинаются на - .
  • С теми, что начинаются на не числовой символ.

Тогда как оператор Венеры работает во всех этих случаях, но имеет меньший приоритет.

Goatse или Сатурн-оператор

=()=

Что делает?

Этот оператор вводит списковый контекст справа от него и возвращает количество элементов слева.

Как работает?

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

$n =()= /word1|word2|word3/g; $n =()= "abababab" =~ /a/; # $n = 1 $n =()= "abababab" =~ /a/g; # $n = 4

К тому же, данный оператор является контейнерным (!), что позволяет запросто «втянуть» в него результат правой части. Это значит, что мы можем поступать следующим образом:

$n =($b)= "abababab" =~ /a/g; # $n = 4; $b = "a" $n =(@c)= "abababab" =~ /a/g; # $n = 4; @c = qw(a a a a)

Следующий «хитрый» пример его использования, похоже, имеет право на жизнь, но существует другой секретный оператор, который может делать то же самое.

Допустим, мы хотим узнать, на сколько частей разобьет строку split, а сами элементы нас не интересуют, может быть, есть смысл попробовать нечто следующего вида:

My $count = split /:/, $string;

Этот пример вернет нам необходимое число, но при этом ругнется на:

Use of implicit split to @_ is deprecated

Для того, чтобы решить данную проблему мы можем воспользоваться данным оператором и написать нечто подобное:

My $count =()= split /:/, $string;

Что не вызовет warning, но при этом будет всегда возвращать 1 потому, что split никогда не разбивает строку на большее количество частей, чем необходимо. В свою очередь, компилятор расценивает попытку сохранить в () как утверждение в том, что данные элементы нам не надо и вернет неизмененную строку, что будет приведено к списку с одним элементом.

Есть два возможных решения.

Первое заключается в том, что мы можем запретить компилятору проводить оптимизацию split() , указав при помощи -1 желание получить бесконечное количество кусков строки:

My $count =()= split /:/, $string, -1;

Или использовать другой секретный оператор, «черепашку»:

My $count = @{[ split /:/, $string ]};

Воздушный змей

~~<>

На самом деле, данный оператор является всего-лишь комбинацией Червяка и <> . Он предоставляет скалярный контекст для операции readline() , но полезный он только в контексте списка.

Богато украшенный двусторонний меч

<> m ;

Этот секретный оператор предоставляет мультистрочные комментарии и не более того. Пример использования:

<> Use the secret operator on the previous line. Put your comments here. Lots and lots of comments. You can even use blank lines. Finish with a single m ;

Но следует учитывать тот факт, что данный комментарий - просто строка, заключенная в двойные кавычки, а потому может иметь некоторые побочные эффекты.

Отверточные операторы

Обнаруженный Дмитрием Карасиком в процессе поиска операторов, базирующихся на! . Как и отвертки, эти операторы бывают четырех основных типов, но с разной длиной рукоятки:

Прямая отвертка - обеспечивает декремент по условию:

$x -=!! $y # $x-- if $y; $x -=! $y # $x-- unless $y;

Крестовая отвертка - инкремент по условию:

$x +=!! $y; # $x++ if $y; $x +=! $y; # $x++ unless $y;

Отвертка-звездочка - сброс переменной в 0 по условию:

$x *=!! $y; # $x = 0 unless $y; $x *=! $y; # $x = 0 if $y;

Крестообразная отвертка-шлиц - сброс переменной в ’’ по условию:

$x x=!! $y; # $x = "" unless $y; $x x=! $y; # $x = "" if $y;

Enterprise-оператор

()x!!

Довольно часто возникает необходимость добавить элемент в список по условию. Это можно сделать следующим образом:

My @shopping_list = ("bread", "milk"); push @shopping_list, "apples" if $cupboard{apples} < 2; push @shopping_list, "bananas" if $cupboard{bananas} < 2; push @shopping_list, "cherries" if $cupboard{cherries} < 20; push @shopping_list, "tonic" if $cupboard{gin};

И узнали, как числа и строки конвертируются друг в друга на лету. Мы даже мельком взглянули на условное выражение if , но пока что не узнали, как сравнить два скаляра. Об этом пойдет речь в этой части.

Если у нас есть две переменные $x и $y, можно ли их сравнить? Равны ли 1, 1.0 и 1.00? А как насчет "1.00"? Что больше - "foo" или "bar"?

Два набора операторов сравнения

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

Вот эти два набора операторов:

Числовое Строковое Значение == eq равно!= ne не равно < lt меньше > gt больше <= le меньше или равно >= ge больше или равно

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

Рассмотрим несколько примеров:

Use strict; use warnings; use 5.010; if (12.0 == 12) { say "TRUE"; } else { say "FALSE"; } В этом простейшем случае Perl выведет TRUE, так как оператор == сравнивает два числа, так что Perl"у не важно, записаны ли они как целые числа, или как числа с плавающей точкой.

В следующем сравнении ситуация немного интереснее

"12.0" == 12

это выражение также истинно, ведь оператор Perl"а == конвертирует строку в число.

2 < 3 истинно, так как < сравнивает два числа. 2 lt 3 также истинно, ведь 2 находистя перед 3 в таблице ASCII. 12 > 3 очевидно, истинно. 12 gt 3 вернет FALSE

Возможно, с первого взгляда кому-то это покажется неожиданным, но если подумать, Perl ведь сравнивает строки посимвольно. Так что он сравнивает "1" и "3", и раз они отличаются и "1" стоит перед "3" в таблице ASCII, на этом этапе Perl решает, строковое значение 12 меньше, чем строковое значение 3.

Всегда нужно быть уверенным, что сравниваешь значение именно так, как нужно!

"foo" == "bar" будет истинно

Также это выдаст предупреждение, если(!) предупреждения включены с помощью use warnings . Причина его в том, что мы используем две строки как числа в числовом сравнении ==. Как упоминалось в предыдущей части, Perl смотрит на строку, начиная с левого конца, и конвертирует ее в число, которое там находит. Поскольку обе строки начинаются с букв, они будут конвертированы в 0. 0 == 0, так что выражение истинно.

С другой стороны:

"foo" eq "bar" ложно

Так что всегда нужно быть уверенным, что сравниваешь значение именно так, как нужно!

То же будет при сравнении

"foo" == "" будет истинно

"foo" eq "" будет ложно

Результаты в этой таблице могут пригодиться:

12.0 == 12 ИСТИНА "12.0" == 12 ИСТИНА "12.0" eq 12 ЛОЖЬ 2 < 3 ИСТИНА 2 lt 3 ИСТИНА 12 > 3 ИСТИНА 12 gt 3 ЛОЖЬ! (внимание, может быть неочевидно с первого взгляда) "foo" == "" ИСТИНА! (Выдает предупреждение, если использована прагма "warnings") "foo" eq "" ЛОЖЬ "foo" == "bar" ИСТИНА! (Выдает предупреждение, если использована прагма "warnings") "foo" eq "bar" ЛОЖЬ

И наконец пример, когда можно попасть в ловушку, получив некоторые данные от пользователя, и, аккуратно отрезав перевод строки в конце, проверить, не является ли строка пустой.

Use strict; use warnings; use 5.010; print "input: "; my $name = ; chomp $name; if ($name == "") { # неверно! здесь нужно использовать eq вместо == ! say "TRUE"; } else { say "FALSE"; }

Если запустить этот скрипт и ввести "abc", мы получим ответ TRUE, так как perl решил, что "abc" это то же, что и пустая строка.

Операторы в Perl-е имеют различный приоритет. Операторы, заимствованные из C , сохранили между собой ту же иерархию, что и в C . Термы имеют самый большой приоритет, они содержат переменные, кавычки, выражения в скобках, функции с их параметрами. Если за списковым оператором (например, print()) или унарным оператором (например, chdir()) следует список аргументов, заключенный в скобки, то эта последовательность имеет самый высокий приоритет, подобно функции с аргументами. Аналогично термам обрабатываются последовательности do{} и eval{} .

3.4.2 Оператор ``стрелка""

Также, как в С и С++ ``- > "" является инфиксным оператором ссылки. Если правая часть является [...] или {...} подпрограммой, тогда левая часть должна быть символьной ссылкой на массив или хэш. Если правая часть - это имя метода или скалярная переменная содержащая имя метода, то левая часть должна быть объектом или именем класса.

П2.3.4.3 Операторы ++ и - -

Эти операторы работают также как и в С. То есть, если они стоят перед переменной, то они увеличивают или уменьшают переменную до возвращения значения. Если они стоят после переменной, то увеличение или уменьшение переменной происходит после возврата значения. Если переменная содержит в себе число или употребляется в скалярном контексте, то использование ++ дает обычное увеличение значения. Если же переменная употреблялась только в строковом контексте, не является пустой строкой и содержит символы a-z,A-Z,0..9 , то происходит строковое увеличение значения переменной:
print ++($foo = "99"); - напечатает 100
print ++($foo = "a0"); - напечатает a1
print ++($foo = "Az"); - напечатает Ba
print ++($foo = "zz"); - напечатает aaa

3.4.4 Экспоненциальный оператор

В Perl-е двойная звездочка ** является экспоненциальным оператором. Он требует к себе даже больше внимания, чем унарный минус: -2**4 это -(2**4) , но не (-2)**4 .

3.4.5 Символьные унарные операторы

Унарный! означает логическое отрицание. Унарный минус, в случае числового значения переменной, обозначает обычное арифметическое отрицание. Если операндом является идентификатор, то возвращается строка, состоящая из знака минус и идентификатора. Если строка начинается со знака + или - , то возвращается строка, начинающаяся с противоположного знака. Унарная тильда `` ~ "" обозначает побитовое отрицание.
Унарный плюс не имеет влияния даже на строки. Он используется для отделения имя функции от выражения заключенного в скобки, которое иначе рассматривается как список аргументов.

Rand (10) * 20; - (rand10) * 20; rand +(10) * 20; - rand(10 * 20); Унарный бэкслэш ``"" обозначает ссылку на то, что стоит за ним.

3.4.6 Операторы связки

Знак равенства с тильдой ``= ~ ""связывает выражение слева с определенным шаблоном. Некоторые операторы обрабатывают и модифицируют переменную $_ . Эти же операции иногда желательно бывает выполнить над другой переменной. Правый аргумент это образец поиска, подстановки или трансляции, левый аргумент - это то, что должно быть подставлено вместо $_ . Возвращаемая величина показывает успех операции. Бинарное ``! ~ "" это тоже самое, что и ``= ~ "", только возвращаемая величина является отрицательной в логическом смысле.

3.4.7 Бинарные операторы

Звездочка * - умножение двух чисел. Cлэш / - деление числа на число. Процент % - вычисляет модуль двух чисел, x - оператор повторения. В скалярном контексте возвращает строку, состоящую из многократно повторенного левого операнда, причем повторяется он то количество раз, которое стоит справа. В списковом контексте он многократно повторяет список. print "a" x 80; напечатает букву a 80 раз.
@ones = (1) x 80; массив из восьмидесяти единиц.
@ones = (5) x @ones сделает все элементы равными пяти.
Бинарный плюс - операция сложения двух чисел.
Бинарный минус - операция вычитания двух чисел.
Бинарная точка - конкатенация строк.

3.4.8 Операторы сдвига

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

3.4.9 Операторы сравнения

``<"" - возвращает TRUE если левый аргумент численно меньше, чем правый.
``>"" - возвращает TRUE если правый аргумент численно меньше, чем левый.
``<="" - возвращает TRUE если правый аргумент численно меньше или равен левому.
``>="" - возвращает TRUE если левый аргумент численно меньше или равен правому.
``gt"" - возвращает TRUE если левый аргумент меньше (в строковом контексте), чем правый.
``lt"" - возвращает TRUE если правый аргумент меньше (в строковом контексте), чем левый.
На поведение операторов lt и gt влияют установки системного языка, если операционная система способна работать с несколькими языками. По этой причине операторы должны корректно работать со строками на языках, отличных от US ASCII , что в системе UNIX задается указанием свойств LC_COLLATE системного locale .

3.4.10 Операторы эквивалентности

Возвращает TRUE , если левый аргумент численно эквивалентен правому.
!= возвращает TRUE , если левый аргумент численно неэквивалентен правому.
<=> возвращает -1, 0 или 1 в зависимости от того, численно меньше, равен или больше левый аргумент правого.
eq возвращает TRUE , если левый аргумент эквивалентен правому (в строковом контексте).
ne возвращает TRUE , если левый аргумент неэквивалентен правому (в строковом контексте).
cmp возвращает -1, 0 или 1 в зависимости от того, меньше равен или больше левый аргумент правого (в строковом контексте).

3.4.11 Побитовое И, побитовое ИЛИ и Исключающее ИЛИ

Бинарное & возвращает объединенные побитово операнды.
Бинарное | возвращает перемноженные побитово операнды.
Бинарное ^ возвращает исключенные побитово операнды.

3.4.12 Логическое И и логическое ИЛИ

Бинарное && - логическое И. Если левый аргумент FALSE , то правый не проверяется.
Бинарное || - логическое ИЛИ. Если левый аргумент TRUE , то правый аргумент не проверяется.
||""и && отличаются от подобных операторов в \verb C| тем, что вместо 0 или 1 они возвращают последнюю обработанную величину. Таким образом, наиболее удобным способом определить домашний каталог пользователя из переменной окружения HOME будет (на практике такой способ определения домашнего каталога пользователя не рекомендуется):

$home = $ENV{"HOME"} || $ENV{"LOGDIR"} || (getpwuid($<)) || die "You"re homeless!\n"; В качестве более удобной для чтения альтернативы Perl поддерживает операторы and и or , которые будут описаны далее. Их приоритет ниже, однако их можно с удобством использовать, не расставляя скобки, после операторов, аргументами которых являются списки: unlink "alpha", "beta", "gamma" or gripe(), next LINE; Если писать в стиле C , то это может быть записано так: unlink("alpha", "beta", "gamma") || (gripe(), next LINE);

3.4.13 Оператор диапазона

Оператор диапазона. Реально это два разных оператора, в зависимости от контекста. В списковом контексте он работает как оператор диапазона от левого аргумента до правого.

For (1..10) { #code } В скалярном контексте он возвращает булевское значение. Если левый операнд TRUE , то.. принимает значение TRUE , если правый операнд тоже TRUE . if (101..200) { print "hi;)";} - напечатает вторую сотню строк

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

?: также как и в C является условным оператором. Он работает подобно if-then-else . Если аргумент перед? - TRUE , то возвращается аргумент перед: , в противоположном случае возвращается аргумент после: . Скалярный или списковый контекст второго и третьего аргументов передается по наследству.

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

3.4.15 Операторы присваивания

Обычный оператор присваивания. Вообще операторы присваивания работают также как и в C . $a += 2; - то же самое, что и $a = $a + 2; Можно использовать следующие сокращения:

**= += *= &= <<= &&= -= /= |= >>= ||= .= %= ^= x= ($a += 2) *= 3; - то же самое, что и: $a = $a + 2; $a = $a * 3;

3.4.16 Оператор ``запятая""

Оператор запятая или comma-оператор. В скалярном контексте он обрабатывает левый аргумент и отбрасывает его значение, потом обрабатывает правый аргумент и возвращает его величину. В этом он подобен comma-оператору из C . В списковом контексте он играет роль разделителя аргументов и вставляет оба аргумента в список. => является синонимом comma-оператора.

3.4.17 Логическое НЕ

Унарное NOT возвращает отрицание аргумента. Оно эквивалентно! , за исключением более низкого приоритета.

П2.3.4.18 Логическое И, ИЛИ и Исключающее ИЛИ

and возвращает конъюнкцию двух выражений. Он эквивалентен && , за исключением более низкого приоритета. or возвращает дизъюнкцию аргументов. Он эквивалентен ||, за исключением более низкого приоритета. xor (eXclusive OR) - исключающее ИЛИ, возвращает истину, если истинен ровно один из аргументов.

3.4.19 Оператор чтения из файла

В Perl есть несколько операций ввода-вывода. Для вывода из файла используется команда <>.

Open(STDIN,"/etc/passwd"); while ($string = ) { @a = split(/[:]/,$string); } Внутри этих скобок стоит дескриптор файла. Считывание происходит построчно. В конце файла принимает значение FALSE и цикл while завершается. По умолчанию считывание происходит в переменную $_ . Нулевой дескриптор файла используется также как в sed и awk , то есть считывается поток из файлов перечисленных в командной строке.

3.4.20 Оператор замены строки

Оператор s/PATTERN/REPLACEMENT/egimosx производит поиск строки, соответствующей шаблону PATTERN и если строка найдена, то подстановку на ее место текста REPLACEMENT . Возвращает количество произведенных подстановок. Если перед этим не использовался оператор =~ или!~ для определения переменной, которая будет обрабатываться, то будет модифицироваться переменная $_ . Этот оператор используется со следующими опциями: e интерпретирует правую часть как выражение. g производит подстановку на место каждой строки, соответствующей шаблону. i производит поиск различающий большие и маленькие буквы. m обрабатывает строку, как состоящую из нескольких строк. o происходит подстановка только на место первой встреченной строки. s обрабатывает строку, как состоящую только из одной строки. x использует расширенные регулярные выражения. Например:

$path =~ s|/usr/local/bin|/usr/bin|; ($foo = $bar) =~ s/this/that/o; $count = ($paragraf =~ s/Mister\b/Mr./gm);

3.4.21 Оператор замены множества символов

tr/SEARCHLIST/REPLACEMENTLIST/cds y/SEARCHLIST/REPLACEMENTLIST/cds Заменяет все найденные символы из множества символов SEARCHLIST на соответствующие символы из множества символов REPLACEMENTLIST . Возвращает число символов, которые были заменены или удалены. Если посредством операторов =~, !~ не была указана никакая строка, то обрабатывается переменная $_ . y является синонимом tr . Если SEARCHLIST заключен в скобки, то REPLACEMENTLIST тоже заключается в скобки, которые могут отличаться от тех, в которые заключается шаблон, например:

Tr tr(+-*/)/ABCD/ Этот оператор употребляется со следующими опциями: c заменяет символы, которые не входят во множество SEARCHLIST на REPLACEMENTLIST , например: tr/a-zA-Z/ /cs; заменит неалфавитные символы. d Стирает символы, которые ни на что не заменяются. s Переводит последовательность символов, которые заменяются на один и тот же символ в один символ. Например: $a = "CCCCCCCCC"; $a =~ tr/C/D/s; теперь $a = "D"

В дополнение к всеобъемлющему списку операторов сравнения строк Sinan Ünür Perl 5.10 добавляет оператор интеллектуального соответствия.

Оператор smart match сравнивает два элемента в зависимости от их типа. См. Диаграмму ниже для поведения 5.10 (я считаю, что это поведение немного меняется в 5.10.1):

perldoc perlsyn "Умное соответствие в деталях" :

Поведение умного соответствия зависит от того, что это за аргументы. Он всегда коммутативен, т.е. $a ~~ $b ведет себя так же, как $b ~~ $a . Поведение определяется следующей таблицей: первая строка, которая применяется в любом порядке, определяет поведение совпадения.

$a $b Type of Match Implied Matching Code ====== ===== ===================== ============= (overloading trumps everything) Code[+] Code[+] referential equality $a == $b Any Code[+] scalar sub truth $b−>($a) Hash Hash hash keys identical ~~ Hash Array hash slice existence grep {exists $a−>{$_}} @$b Hash Regex hash key grep grep /$b/, keys %$a Hash Any hash entry existence exists $a−>{$b} Array Array arrays are identical[*] Array Regex array grep grep /$b/, @$a Array Num array contains number grep $_ == $b, @$a Array Any array contains string grep $_ eq $b, @$a Any undef undefined !defined $a Any Regex pattern match $a =~ /$b/ Code() Code() results are equal $a−>() eq $b−>() Any Code() simple closure truth $b−>() # ignoring $a Num numish[!] numeric equality $a == $b Any Str string equality $a eq $b Any Num numeric equality $a == $b Any Any string equality $a eq $b + − this must be a code reference whose prototype (if present) is not "" (subs with a "" prototype are dealt with by the "Code()" entry lower down) * − that is, each element matches the element of same index in the other array. If a circular reference is found, we fall back to referential equality. ! − either a real number, or a string that looks like a number

Разумеется, "код соответствия" не представляет собой настоящий код соответствия: он просто объясняет предполагаемое значение. В отличие от grep, оператор smart match будет замыкаться каждый раз, когда это возможно.

Пользовательское сопоставление через перегрузку Вы можете изменить способ сопоставления объекта путем перегрузки оператора ~~ . Это превосходит обычную интеллектуальную семантику соответствия. См. overload .

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

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

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

$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 "$_" ;
}

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