Некоторые сведения о Perl 5/Операторы
← Операции и выражения | Глава | Ввод и вывод → |
Операторы | ||
Perl является императивным языком, т.е. программа строится из последовательности операторов (англ. statements), определяющих действия. Операторы бывают простыми и составными.
Простыми операторами представляются различные выражения. Простой оператор также может быть снабжен модификатором. Простыми операторами например являются присваивание значения и арифметическое выражение.
Составные операторы работают в терминах блоков. Составными операторами являются, например, ветвления.
Операторы обрабатываются в той последовательности, в которой они записаны в программе. Некоторые операторы позволяют также управлять точкой следования программы.
В Perl любой оператор должен завершаться точкой с запятой, подобно языку Си. Если оператор является последним в блоке, то точку с запятой можно опускать, например сравните
{
$a = 2;
$b = 3;
print $a + $b, "\n" # Это последнее выражение в данном блоке — точку с запятой можно опустить
}
print "Hello, World!", "\n" # Код вне явно обозначенного блока существует в неявном блоке и эта последнее выражение в нем.
Тем не менее, рекомендуется всегда проставлять точку с запятой, потому что это самая частая ошибка компиляции и об этом символе очень легко забыть в большой программе.
Простые операторы
[править]Простой оператор представляет собой выражение, завершаемое точкой с запятой ;
. Основное назначение простого оператора — вычисление выражения с некоторым побочным эффектом (т.е. действия, изменяющего среду исполнения программы). Побочного эффекта в общем случае может не быть.
В языке Perl к простым операторам можно применять модификаторы, причем только один за раз. Модификаторы являются своего рода синтаксическим сахаром в Perl. Все модификаторы подставляются в конец простого оператора. Модификаторы позволяют обеспечить тот самый минимализм в написании выражений.
Ниже представлены все модификаторы, которые есть в Perl:
if <выражение>
иunless <выражение>
- Оператор будет исполняться в том случае, если
<выражение>
ИСТИНА (дляif
) и ЛОЖНО — дляunless
. Данный модификатор позволяет выразить ветвление, когда нет большого блока с операторами.
- Оператор будет исполняться в том случае, если
while <выражение>
иuntil <выражение>
- Повторно вычисляет оператор, если
<выражение>
ИСТИНА (дляwhile
) и ЛОЖНО — дляuntil
. Данный модификатор позволяет выразить цикл, когда нет большого блока с операторами.
- Повторно вычисляет оператор, если
foreach <выражение-список>
- Позволяет применить один и тот же оператор к каждому элементу списка, получаемого из
<выражение-список>
. Позволяет компактно выразить циклforeach
, когда действие над элементами списка простое.
- Позволяет применить один и тот же оператор к каждому элементу списка, получаемого из
when <выражение>
(начиная с 5.12)- Используется в конструкциях
given..when
для объявления условия (см. описание этой конструкции ниже).
- Используется в конструкциях
Многие модификаторы по ключевым словам совпадают с одноименными составными операторами, о которых речь пойдет ниже. Следует помнить, что это только внешняя схожесть, так как парсятся Perl они по-разному.
Ниже показано несколько показательных примеров использования модификаторов.
$x = 5;
print $x++, "\n" if $x == 5; # Напечатает 5. В выражении с print есть побочный эффект, который увеличит переменную на 1
print $x, "\n" unless $x == 5; # Напечатает 6.
print $x++, "\n" while $x <= 10; # Будет печатать, пока переменная не дорастет до 10.
print $x--, "\n" until $x == 0; # Будет печатать, пока переменная не станет 0.
print "$_\n" foreach "apple","banana","grapes"; # Распечатает список.
Составные операторы
[править]Составные операторы работают с блоками. С их помощью реализуются ветвления и циклические вычисления.
Блок – это последовательность операторов, заключенная в фигурные скобки, определяющих еще одну область видимости переменных. Интерпретатор Perl всегда рассматривает блок как один оператор. Результатом всего блока является результат последнего оператора внутри него. Это свойство позволяет вам использовать блок там, где ожидается один оператор. Следует помнить, что результат вычисления отдельных операторов внутри блоков временно сохраняется в переменной $_
, чем иногда можно пользоваться в следующем по отношении к этому оператору операторе. Соответственно результат самого последнего оператора в блоке возвращается наружу.
Область видимости, порождаемая блоком, позволяет вам создавать переменные, которые будут видны только в этом блоке, т.е. создавать локальные переменные. Локальные переменные создаются с помощью функции my()
, параметром которой является список переменных, которые нужно сделать локальными. Такие переменные автоматически удаляются при выходе из блока и уберегают вас от утечек памяти. Следует отличать локальные переменные, создаваемые my()
, от переменных, создаваемых local()
(иногда их называют динамическими). Последняя позволяет вам сохранить предыдущее значение перед входом в блок, а потом восстановить его после выхода из блока, тогда как my()
определяет именно время жизни и видимость переменной.
Ветвления
[править]В языке Perl для реализации ветвлений предусмотрен только оператор if
, который имеет несколько форм.
if (<выражение>) <блок>
if (<выражение>) <блок_1> else <блок_2>
if (<выражение>) <блок_1> elsif (<выражение_2>) <блок_2> ... else <блок_N>
Обратите внимание, что везде, где мы говорим блок, подразумеваются фигурные скобки (потому что именно они порождают блок), даже если в блоке всего один оператор. Это отличает Perl от того же Си, где фигурные скобки можно опускать, когда в блоке один оператор.
В простой форме if
записанный после него блок исполняется, когда <выражение>
возвращает ИСТИНУ. ИСТИНОЙ не обязательно должен быть результат булева выражения: им может быть результат любого выражения, в том числе и простого присваивания. ИСТИНОЙ считается ненулевое значение и не пустая строка, в противном случае это ЛОЖЬ. Когда в выражении записывается операция, то проверяется возвращаемый ей результат.
Вторая форма вводит альтернативную ветвь, через ключевое слово else
. Блок этой ветви исполняется, если выражение основной ветви оказалось ЛОЖНЫМ. Если требуется ввести несколько альтернативных ветвей, то используется ключевое слово elsif
, после которого записывается выражение по этой ветви.
Все ветви в этом ветвлении просматриваются по порядку, пока одна из них не окажется ИСТИНОЙ, либо пока они не закончатся. В любом случае ветвь else
исполняется всегда, если она есть и до нее все выражения вернули ЛОЖЬ.
Ключевое слово if
может быть заменено на unless
, например
unless (<выражение>) <блок_1> elsif (<выражение_2>) <блок_2> ... else <блок_N>
тогда первое условие будет проверяться на ЛОЖНОСТЬ.
Все ветвления можно вкладывать в блоки, порождая множество слоев из проверок.
Циклы
[править]Циклы while и until
[править]Циклы while
и until
предназначены для повторного вычисления блока операторов, пока остается истинным задаваемое в нем условное выражение. Общий синтаксис циклов такой
<метка> [ while | until ] (<условие>) <блок>
<метка> [ while | until ] (<условие>) <блок_1> continue <блок_2>
Блок цикла while
исполняется до тех пор, пока условное выражение возвращает ИСТИНУ, а until
— пока выражение возвращает ЛОЖЬ. Обычно в блок закладывается выражение, которое рано или поздно сделает условие ИСТИННЫМ / ЛОЖНЫМ. В крайнем случае цикл может быть бесконечным, тогда исполнение прерывается иными способами.
Начало цикла может сопровождать метка, завершаемая двоеточием :
для реализации безусловных переходов. Блок continue
выполняется всякий раз, когда осуществляется переход на выполнение новой итерации. Этот блок используется редко: обычно туда закладывают изменения параметров для основного условия, вместо того, чтобы это делать в основном блоке.
Примеры.
$i = 1;
print "While [1]:\n";
while ($i <= 5) {
print $i . " ";
++$i;
}
# While [1]:
# 1 2 3 4 5
print "\nUntil [1]:\n";
until ($i == 1) {
print $i . " ";
--$i;
}
# Until [1]:
# 6 5 4 3 2
print "\nWhile [2]:\n";
while ($i <= 5) {
print $i . " ";
} continue {
++$i;
}
# While [2]:
# 1 2 3 4 5
print "\nUntil [2]:\n";
until ($i == 1) {
print $i . " ";
} continue {
--$i;
}
# Until [2]:
# 6 5 4 3 2
Обратите внимание, что ключевые слова while
и until
существуют также в форме модификаторов (рассмотренных ранее), семантически очень похожих на конструкции циклов. Обычно они прикрепляются к конструкции do
, чтобы строить циклы с постусловием в стиле do..while
и do..until
. В таких циклах условие проверяется в конце итерации, а не в начале, т.е. хотя бы одно повторение всегда будет выполнено.
$i = 1;
print "\nDo..while:\n";
do {
print $i . " ";
++$i;
} while ($i <= 5);
# Do..while:
# 1 2 3 4 5
print "\nDo..until:\n";
do {
print $i . " ";
--$i;
} until ($i == 1);
# Do..until:
# 6 5 4 3 2
Цикл for
[править]Цикл for
используется, когда число повторений заранее известно, либо условие удобно выразить через счетчики. Общий синтаксис у него такой
<метка> for (<инициализация> ; <условие> ; <итерация>) <блок>
В части <инициализация>
устанавливаются начальные значения переменных, управляющих циклом. Обычно это одна или более переменных, разделенных запятыми. В части <условие>
проверяется условие на основе переменных из <инициализация>
. Пока это условие ИСТИНА, будет исполняться выражения из <блок>
. В части <итерация>
происходит увеличение или уменьшение переменных из <инициализация>
, чтобы сделать <условие>
ЛОЖНЫМ. Часть <итерация>
исполняется в конце каждого прогона, а <инициализация>
исполняется один раз при входе в цикл.
Пример
for ($i = 2, $j = 2; $j <= 9; $j++) {
print $i, "*", $j, "=", $i * $j, "\n";
}
# 2*2=4
# 2*3=6
# 2*4=8
# 2*5=10
# 2*6=12
# 2*7=14
# 2*8=16
# 2*9=18
Цикл for
имеет полезное свойство, позволяющее определять переменные инициализации локально. В предыдущем примере наши счетчики были объявлены глобально, т.е. время их жизни равно времени жизни программы. Однако нам хотелось бы, чтобы счетчики автоматически удалялись из памяти после выполнения цикла. Для этого их нужно объявить с помощью функции my
(подробнее о видимости переменных мы поговорим позже).
for (my $i = 2, my $j = 2; $j <= 9; $j++) {
print $i, "*", $j, "=", $i * $j, "\n";
}
print $j, $i, "\n" # Будет напечатана пустота, так как счетчики имеют локальную видимость
Все три части в скобках для цикла for
являются необязательными. Этим можно воспользоваться, чтобы сделать цикл бесконечным
for (;;) {
...
}
Однако, на практике считается более устоявшимся делать бесконечный цикл из while
из-за лучшей читаемости
while (1) {
...
}
# или
while () {
...
}
Кроме того, из for
можно делать эквивалент while
, если объявлять только одно условие. Но, такое использование плохо сказывается на читаемости конструкции, поэтому оно не рекомендуется.
Цикл foreach
[править]Частой задачей на практике является полный перебор массивов и хеш-массивов. Эту задачу можно выполнять через цикл for
, но в этом случае нам приходится самостоятельно контролировать границы перебора через переменные. Цикл foreach
был придуман, чтобы облегчить задачу полного перебора, так как границы он проверяет автоматически. Синтаксис цикла имеет следующий вид:
<метка> foreach <переменная> (<список>) <блок>
<метка> foreach <переменная> (<список>) <блок_1> continue <блок_2>
Данный цикл перебирает <список>
, пока он не закончится, последовательно помещая очередной элемент в <переменная>
. Блок операторов continue
выполняется всякий раз, когда начинается очередная итерация, за исключением самой первой. Данным циклом можно перебирать массивы, хеш-массивы или простые списки слов, разделенные запятыми.
Примеры:
foreach $element ("apple","banana","grapes") {
print $element, "\n";
}
@arr = (1..5);
foreach $number (@arr) {
print $number, "\n";
}
%colors = (
red => 1,
green => 2,
blue => 3,
);
# Мы использовали функцию сортировки, чтобы порядок обращения к хеш-массиву был
# детерминирован.
foreach $color (sort keys %colors) {
print $color, "=", $colors{$color}, "\n";
}
На практике, чтобы ускорить написание кода, можно игнорировать написание переменной. В этом случае каждый элемент будет записываться внутрь встроенной переменной $_
. Этот прием следует запомнить.
foreach ("apple","banana","grapes") {
print $_, "\n";
}
Кроме того, слово foreach
можно сокращать просто до слова for
. Конечно это дело вкуса, но по мнению автора полная форма визуально лучше передает намерения программиста.
Цикл foreach
автоматически делает свою переменную локальной, т.е. она создается только на время выполнения цикла. Также следует помнить, что переменная реально отсылает к значению в памяти, т.е. делая изменения в переменной, вы изменяете значение в памяти. Этим можно пользоваться, когда нужно сочетать перебор с изменением значений в массивах.
Помните, что foreach
перебирает массивы всегда быстрее for
с индексами, так как нет накладных расходов по их вычислению. Всегда отдавайте foreach
предпочтение, когда списки требуется перебирать целиком.
Управление циклом
[править]Иногда нужно вмешиваться в исполнение блока цикла, обычно когда условия достигаются раньше, чем обозначены в условии цикла. В таких случаях часть выражений в блоке нужно опускать и начинать новую итерацию, либо совсем прерывать цикл. Для этого предусмотрены следующие управляющие слова, применимые к любому вышеописанным циклам.
last <метка>
- Прерывает исполнение цикла в текущей точке следования и переносит исполнение в точку, следующую за данным циклом, либо за цикл по его метке, когда она указана. Данное слово аналогично оператору
break
во многих других языках программирования.
- Прерывает исполнение цикла в текущей точке следования и переносит исполнение в точку, следующую за данным циклом, либо за цикл по его метке, когда она указана. Данное слово аналогично оператору
next <метка>
- Прерывает исполнение блока цикла и переносит точку следования в начало текущего цикла, чтобы начать новую итерацию, либо для цикла по метке, когда она указана. Данное слово аналогично оператору
continue
во многих других языках программирования.
- Прерывает исполнение блока цикла и переносит точку следования в начало текущего цикла, чтобы начать новую итерацию, либо для цикла по метке, когда она указана. Данное слово аналогично оператору
redo <метка>
- Необычное слово, которое не встречается в других языках программирования. Служит, чтобы исполнить текущую итерацию, не изменяя ее начальных условий (т.е. не инициализируя новую итерацию). Это напоминает выражение дать еще один шанс. Если указана метка, то переносит точку для цикла по метке.
Чаще всего метки используются, когда исполняется несколько вложенных циклов, и требуется, например, прервать вложенный и перейти на вышестоящий по метке. Обратите внимание, что если у цикла есть метка, то переход будет не на сам оператор цикла, а на первый оператор после него. Во всех трех управляющих словах передается только идентификатор метки (т.е. без двоеточия). Отметим, что к данным управляющим словам разрешено применять модификаторы, например next if $i == 4;
.
Если цикл имеет блок continue
, то при переходе по управляющему слову next
он всегда будет исполняться в начале. Именно поэтому в сложных циклах следует выносить операции, связанные с условиями цикла, в этот блок. С другой стороны, если переход происходит по слову redo
, то блок continue
не исполняется и вообще текущее условное состояние цикла не изменяется. Блок continue
также не будет исполняться и по last
.
Именованные блоки
[править]В Perl любому блоку можно присвоить метку, как это делается для циклов:
BLOCK1: {
...
}
Блоки с меткой называются именованными. Синтаксически это эквивалентно циклу с одной итерацией, поэтому к блоку можно даже прикреплять блок continue
и использовать управляющие слова, рассмотренные ранее:
BLOCK1: {
$i = 1;
next BLOCK1;
} continue {
$i++;
}
print $i, "\n";
# Результатом будет 2, потому что по next блок continue исполняется.
Чаще всего такие блоки используются, чтобы порождать конструкции типа switch..case
, так как отдельно такой конструкции нет в Perl (точнее, поддержку такой конструкции требуется включить).
SWITCH: {
$case_1 = 1, last SWITCH if $variable == 1;
$case_2 = 2, last SWITCH if $variable == 2;
$case_3 = 3, last SWITCH if $variable == 3;
$default_case = 0;
}
Здесь, в зависимости от того, чему равна входящая переменная $variable
, одна из исходящих переменных получит некоторое значение и произойдет выход из блока по last
. Если ни один из модификаторов if
не сработает, то переменной $default_case
будет присвоен 0 и выход из блока произойдет обычным путем.
При определенном упорстве можно из именованных блоков строить циклы, основанные только на переходах. Хотя они будут выглядеть ужасно не читаемыми, язык позволяет вам так делать.
Given..when
[править]Начиная с версии 5.10.1, в интерпретатор была встроена (и стабилизирована) поддержка экспериментальной конструкции given..when
, которая призвана упростить написание ветвлений по типу switch..case
в других языках программирования. Эта синтаксическая конструкция пришла в Perl 5 из спецификации языка Perl 6 и является экспериментальной (т.е. она может быть в дальнейшем оставлена или вырезана из интерпретатора).
Так как конструкция экспериментальная, то по умолчанию она отключена. Чтобы её включить, следует использовать директиву
use feature "switch";
Эта директива включается автоматически, если вы используете директиву use
для указания требования минимальной версии интерпретатора Perl
use v5.10; # Между версиями 5.10 и 5.34 директива 'use feature "switch";' добавляется автоматически.
# Примечание:
# О директивах будет рассказано в последующих главах.
Общий синтаксис конструкции выглядит так:
use feature "switch";
given (<выражение>) {
when (<выражение_1>) { <действия_1>; }
when (<выражение_2>) { <действия_2>; }
...
default { <действия_по_умолчанию>; }
}
- После ключевого слова
given
записывается выражение, результат которого по порядку сравнивается с результатом каждого выражения после ключевого словаwhen
. Чтобы сравнивать выражения, используется операция умного связывания~~
(например,<выражение> ~~ <выражение_1>
), которая была введена с версии 5.10. Умное связывание берет в расчет фактические типы данных, которые получаются после вычисления выражений, что позволяет корректно сравнивать (возможно) разные типы данных. Умное связывание возвращает 1 в случае ИСТИНЫ и""
для ЛЖИ. Обычно в качестве выражений для блоковwhen
используются константы регулярных выражений, но это не обязательно для умного связывания. - Когда умное связывание возвращает ИСТИНУ для некоторого
when
, то исполняются инструкции, записанные в блоке после него, после чего происходит выход из всей конструкции, иначе происходит переход к следующемуwhen
и операция повторяется. Если ни один из блоковwhen
не был исполнен, то исполняется блокdefault
, если конечно он есть. Внутри блока с действиями Perl неявно использует управляющее словоbreak
в конце блока, поэтому вам не нужно писать его явно, тем не менее, если вам нужно прервать исполнения в блоке в некоторой точке, вы можете вызыватьbreak
явно. Если вам нужно продолжить движение по оставшимсяwhen
, вы можете вызвать управляющее словоcontinue
. - Вся конструкция
given..when
возвращает последнее вычисленное внутри него выражение. - Выражения после
given
иwhen
всегда исполняются только в скалярном контексте. - С версии 5.14 безопасно можно вместо
given
писать ключевое словоforeach
иfor
, в частности. Это никак не повлияет на поведение и скорее дело вкуса. - С версии 5.12 ключевое слово
when
существует также в форме модификатора, что позволяет вам записывать всю конструкцию так:# Только с версии 5.12 given (<выражение>) { <действие_1> = 1 when <выражение_1>; ... }
Ниже приведено несколько примеров использования данной конструкции.
use v5.12; # для включения поддержки конструкции given..when
use strict; # для отключения небезопасных конструкций языка Perl (подробнее об этом в следующих главах)
no warnings 'experimental'; # в данном примере это подавит вывод предупреждений об экспериментальных возможностях
print 'Enter something: ';
chomp( my $input = <> ); # Ждем ввод из STDIN
print do {
given ($input) { # Анализируем ввод
"The input has numbers\n" when /\d/; # Если в веденной строке хотя бы одна цифра, то сработает этот when.
"The input has letters\n" when /[a-zA-Z]/; # Если нет цифр и есть буквы латинского алфавита, то сработает этот when.
default { "The input has neither number nor letter\n"; } # В остальных случаях этот.
}
}
$ perl given.pl
Enter something: abc123
The input has numbers
$ perl given.pl
Enter something: abc
The input has letters
$ perl given.pl
Enter something:
The input has neither number nor letter
Следующий пример делает примерно то же самое, но анализирует только числа. Сама конструкция записана в другом стиле.
use v5.12; # для включения поддержки конструкции given..when
use strict; # для отключения небезопасных конструкций языка Perl (подробнее об этом в следующих главах)
no warnings 'experimental'; # в данном примере это подавит вывод предупреждений об экспериментальных возможностях
print 'Enter a number: ';
chomp( my $input = <> ); # Ждем ввод из STDIN
print do {
given ($input) { # Анализируем ввод
when ($input > 0) { # Обратите внимание, что выражение является условием.
"$input is a positive number\n";
}
when ($input < 0) {
"$input is a negative number\n";
}
default {
"$input is NaN\n";
}
}
}
Следующий пример демонстрирует, что умное связывание также умеет сравнивать как литералы, так и числа.
use v5.12; # для включения поддержки конструкции given..when
use strict; # для отключения небезопасных конструкций языка Perl (подробнее об этом в следующих главах)
no warnings 'experimental'; # в данном примере это подавит вывод предупреждений об экспериментальных возможнос;
print 'Enter the secret word: ';
chomp( my $input = <> ); # Ждем ввод из STDIN
print do {
given ($input) {
when ("secret word") {
"Oh, You are smart\n";
}
when (10) {
2**2 . "\n";
}
default {
"You shall not pass\n";
}
}
}
$ perl given.pl
Enter the secret word: secret word
Oh, You are smart
Следующий пример демонстрирует, что при желании ключевое слово given
может быть заменено на for
:
foreach $element ("string", 25) {
for ($element) {
when (/\d+/) {
print "'$element' is a number\n";
}
when (/\w+/) {
print "'$element' is a string line\n";
}
}
}
'string' is a string line
'25' is a number
Try..catch
[править]Начиная с версии 5.34, была добавлена еще одна экспериментальная конструкция try..catch
для перехвата и обработки исключительных ситуаций (exceptions). Напомним, что исключительной ситуацией называется такая, которая в обычных условиях привела бы к принудительному завершению процесса со стороны операционной системы, но в программе закладывается её обработка, чтобы этого не происходило (именно поэтому такая ситуация и называется исключением).
До этого в программах Perl фатальные ошибки обычно старались предсказать с помощью нагромождения сложных условий, которые не позволяли бы привести к ней. Кроме этого, в Perl есть функция eval
, которая позволяет изолировать код, способный приводить к исключительной ситуации: в этом случае происходит аварийное завершение только функции eval
, а не всей программы.
Общий синтаксис конструкции выглядит так:
use feature 'try';
try {
<код_способный_сгенерировать_исключение>
} catch ($message) {
<обработчик_исключения>
}
finally { # ТОЛЬКО с версии 5.36
<блок_который_выполняется_после_выхода_из_конструкции>
}
- Ключевое слово
catch
всегда должно следовать за блокомtry
и вводить переменную, в которую будет записано сообщение об исключительной ситуации. Эта переменная будет видна только блокуcatch
. - В блоках
try
иcatch
разрешается использование операторов управления точкой исполнения (return
,goto
,next
и другие). В случаеreturn
происходит возврат из исполняемой функции, что отличает от поведения оператора внутриeval
, где его вызов прерывает исполнение только самойeval
и возвращает управление функции, которая вызвалаeval
. - Вся конструкция возвращает последнее вычисленное значение, что может быть использовано для генерации значения с последующим присваиванием его вызывающей стороне. Вызывающей стороне не видны объекты, объявленные в конструкции, что в общем то логично. Также вызывающей стороне не может быть передан результат оператора
return
, так как конструкцияtry..catch
в своей реализации не перехватывает его вызов. - Опционально может быть добавлен блок
finally
, поведение которого напоминает аналогичную конструкцию в языке Java, т.е. всегда исполняет некоторый код после выхода из всей конструкции (даже в случаеreturn
иgoto
). Использование этого блока аналогично использованию функцииdefer()
. - Внутри блока
finally
запрещено пользоваться операторами управления точкой исполнения (return
,goto
,next
и другие).
Внутри блока try
может быть сгенерировано исключение при помощи функции die()
. Ниже показан пример использования конструкции.
use feature ':5.34'; # Для включения конструкции try..catch
no warnings "experimental::try"; # В данном случае мы подавляем вывод предупреждений при использовании экспериментальных конструкции try..catch
try {
# Полагаем, что файла с именем 'filename.txt' не существует.
open FILE, "filename.txt" or die "File cannot be opened: $!"; # Генерируем исключение
while () {
# До сюда мы не доберемся
last;
}
close FILE;
} catch($e) {
print "ERROR: $e";
}
# Продолжаем исполнение
К сожалению, эта конструкция ещё относительно нова и новые версии интерпретатора могут реализовывать её неправильно, поэтому на практике для релизов младших 5.34 следует по старинке использовать eval
. Конструкцию try..catch
при желании можно эмулировать, используя гибкость самого языка. Внимательно рассмотрите следующий пример.
# Для эмуляции try..catch конструкции мы можем объявить следующие вспомогательные функции
sub try(&$) {
my ($code, $catch) = @_;
eval {
&$code;
};
if ($@) { # Если внутри eval была вызвана die, то $@ будет не пустой.
local $_ = $@;
&$catch(); # Вызываем перехватчик исключения
}
}
# Эта функция эмулирует секцию catch конструкции.
sub catch(&) { $_[0]; }
# Теперь тот же код мы можем записать так.
try {
open FILE, "filename.txt" or die "File cannot be opened: $!";
while () {
# До сюда мы не доберемся
last;
}
close FILE;
} catch {
print("Caught an exception: $_")
};
$ perl try.pl
Caught an exception: File cannot be opened: No such file or directory at try.pl line 17.
В данном примере мы объявили функцию try
(используя прототип, для проверки его компилятором), которая первым аргументом может принимать анонимный код, а вторым символическую ссылку на функцию, которую мы разыменовываем внутри try
, когда функция eval
прерывается исключением. Визуально действительно очень похоже на try..catch
, и единственное что выдает в ней функцию, это обязательная точка с запятой в конце. Единственный недостаток такого подхода в том, что во все проекты мы вынуждены тащить эти вспомогательные функции.
К сожалению, мы не можем эмулировать блок finally
через вспомогательные функции. Для реализации finally
вы должны использовать функцию defer()
.
Безусловные переходы
[править]В Perl реализован оператор безусловного перехода goto
, который, как не сложно догадаться, переносит точку следования в другое место программы. На практике следует избегать частого использования этого оператора, так как он делает программу запутанной.
Оператор goto
существует в следующих формах:
goto <метка>;
- Переносит точку следования на любую объявленную метку в программе. Единственное, куда нельзя делать безусловный переход, это циклы
foreach
и процедуры, объявленные черезsub
, так как внутренняя реализация требует некоторой инициализации для них. Отметим, что компилятор Perl не выводит ошибок, если вы попытаетесь сделать переход на несуществующую метку или переход в запрещенное место. Об ошибке вы узнаете только во время исполнения программы.instr1: print "Hello "; goto instr3; instr2: print "Skipped\n"; instr3: print "World!\n"; # Результат: # Hello World!
- Переносит точку следования на любую объявленную метку в программе. Единственное, куда нельзя делать безусловный переход, это циклы
goto <выражение>;
- Это так называемый вычисляемый переход, который также есть в языке FORTRAN. Здесь метка вычисляется в выражении и переход будет по метке, которая получается в результате.
instr1: print "Hello "; goto "instr" . "2"; # Вычисляется метка instr2 instr3: print "World!\n"; instr2: print "John\n"; # Результат: # Hello John
- Это так называемый вычисляемый переход, который также есть в языке FORTRAN. Здесь метка вычисляется в выражении и переход будет по метке, которая получается в результате.
goto &<процедура>;
- Самый сложный для понимания переход. Данный переход подставляет вызов процедуры, которая указана в операторе, в процедуре, которая выполняется сейчас, и скрывает факт того, что вызывалась другая процедура.
sub callee() { my ($nameOfCaller) = @_; my $name = (caller(0))[3]; print "The '$nameOfCaller' calls '$name'"; } sub callable() { local @_ = (caller(0))[3]; # Примечание: мы используем встроенную функцию caller, чтобы получить имя функции, которая # исполняется в текущей точке. goto &callee; } callable(); # Результат: # The 'main::callable' calls 'main::callee'
- Самый сложный для понимания переход. Данный переход подставляет вызов процедуры, которая указана в операторе, в процедуре, которая выполняется сейчас, и скрывает факт того, что вызывалась другая процедура.
В любом случае старайтесь избегать безусловных переходов, так как практика показывает, что любую задачу можно структурировать без них , используя только условия и циклы.
← Операции и выражения | Ввод и вывод → |