Глава 5.Условные операторы и циклы

Краткое введение

В этой главе рассказывается, как с помощью условных операторов и циклов управлять порядком выполнения команд Perl. Кроме того, будут рассмотрены и другие команды, управляющие ходом выполнения сценария — например, goto, exit и die.

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

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

Например, следующий фрагмент сценария использует команду if для проверки значения переменной $variable. Если значение равно пяти, выводится текст «Yes, it is five.». В противном случае на экране появляется строка «No, it is not five.»:

$variable = 5;

if ($variable == 5) {

   print "Yes, it’s five.\n";

} else {

   print "No, it’s not five.\n";

}

Yes, it’s five.

Даже этот простой пример показывает возможности команды if. Она проверяет условие, заданное в круглых скобках и, если результат вычислений соответствует истине (то есть не является нулем, пустой строкой и т. д.), программа будет выполнять первый блок кода. В противном случае осуществляется переход к блоку else (он может и отсутствовать).

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

$variable = 5;

if ($variable == 5) 

{

   print "Yes, it’s five.\n";

}

else

{

   print "No, it’s not five.\n";

}

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

$variable = 5;

if ($variable == 5)       # Это неправильно!

   print "Yes, it’s five.\n";

else

   print "No, it’s not five.\n";

Условные операторы, подобные if, позволяют управлять порядком выполнения программы. В этом и есть суть программирования — правильно принять решение.

Операторы цикла

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

Цикл while уже не раз встречался в этой книге. В следующем примере он используется для построчного вывода содержимого входного файла:

while (<>) {

   print;

}

Более сложные циклы могут использовать индекс цикла. Например, этот цикл for вычисляет значение факториала:

$factorial = 1;

for ($loop_ind = 1; $loop_ind <= 6; $loop_ind++) {

   $factorial *= $loop_ind;

}

print "6! = $factorial\n";

6! = 720

а этот цикл foreach позволяет перебрать указанный список значений:

foreach $variable1 ("one", "two", "three")

{

   print "/$variable1/";

}

/one//two//three/

С помощью индекса цикла можно ссылаться на данные из некоторого набора, последовательно обрабатывая значение за значением. В качестве примера приведем цикл, перебирающий элементы  массива:

@array = ("one", "two", "three");

for ($loop_ind = 0; $loop_ind <= &#array; $loop_ind++)

{

   print $array[$loop_ind] . " ";

}

one two three

Операторы цикла способны выполнять и более сложные действия — так, блок continue, помещенный в конце цикла, задает последовательность операций, которая выполняется в любом случае (точнее, почти в любом случае) после завершения основной части цикла:

@array = ("one", "two", "three");

$loop_ind = 0;

while ($loop_ind <= &#array)

{

   print $array[$loop_ind] . " ";

}

continue

{

   $loop_ind++;

}

one two three 

Команда redo, указанная в теле цикла или разделе continue, передает управление в начало тела цикла (минуя заголовок цикла), команда last обеспечивает выход из цикла, минуя раздел continue, команда next передает управление разделу continue с последующим переходом к новой итерации (если только в этом разделе нет других команд передачи управления или итерация не является последней). Пример — «ручное» управление вырожденным циклом for, имитирующее работу цикла `foreach $index (1, 2, 3, 4, 5)’:

for ($index = 1; $index == 1; $index = 1) {

   if ($index > 5) {last}; # выход из цикла

      print "Hello";

   if ($index == 5) {next}; # переход сразу к continue

      print "... ";

} continue {

   $loop_index++;  # изменение индекса

   redo;   # вернуться в начало цикла, игнорировав

           # команды, указанные в заголовке цикла

}

print "!\n";

Hello... Hello... Hello... Hello... Hello.! 

(Обратите внимание, как команда redo в блоке continue обходит заголовок цикла при передаче управления в начало тела цикла — в противном случае проверка индекса и присвоение индексу единицы испортили бы «нормальную» работу этого примера.)

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

Немедленные решения

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

Оператор if является базовым условным оператором в Perl. Он проверяет условие, заданное в круглых скобках, и, если результат вычислений дает ненулевое значение, выполняется блок команд, ассоциированный с данной командой. (Пустые строки, неопределенные переменные, значения undef и т. д. в данном контексте также рассматриваются как логический ноль.) Можно также задать блок команд, выполняемых в случае ложности проверяемого условия. Это делается с помощью блока else. Конструкция elsif (обратите внимание: не else if и не elseif) продолжает проверку дополнительных условий. Вот как записывается эта команда:

if (выражение) {блок}

if (выражение) {блок} else {блок}

if (выражение) {блок} elsif {блок} ... else {блок}

Рассмотрим пример. Оператор проверки на равенство оценивает, равно ли значение указанной переменной пяти, и если это так, сообщает о результате пользователю:

$variable = 5;

if ($variable == 5) {

   print "Yes, it’s five.\n";

}

Yes, it’s five.

Можно добавить дополнительный раздел else, который будет информировать пользователя о том, что проверка не прошла:

$variable = 6;

if ($variable == 5) {

   print "Yes, it’s five.\n";

} else {

   print "No, it’s not five.\n";

}

No, it’s not five.

Наконец, для выполнения произвольного количества проверок можно добавить разделы elsif:

$variable = 2;

if ($variable == 1) {

   print "Yes, it’s one.\n";

} elsif ($variable == 2) {

   print "Yes, it’s two.\n";

} elsif ($variable == 3) {

   print "Yes, it’s three.\n";

} elsif ($variable == 4) {

   print "Yes, it’s four.\n";

} elsif ($variable == 5) {

   print "Yes, it’s five.\n";

} else {

   print "Sorry, can’s match it!\n";

}

Yes, it’s two

Команда unless

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

unless (выражение) {блок}

unless (выражение) {блок} else {блок}

unless (выражение) {блок} elsif {блок} ... else {блок}

Вот пример с использованием цикла while:

while (<>) {

   chomp;

   unless (/^q/i) {

      print;

   } else {

      exit;

   }

}

Эта программа печатает текст, который вводится пользователем, если только пользователь не введет строчку, начинающуюся с литер q или Q (сокращение от quit или QUIT). В противном случае работа программы прекращается. Проверка условия выполняется за счет сравнения текста, хранящегося в переменной $_, с шаблоном (шаблоны и условия совпадения текста с шаблоном рассматриваются в главе 6).

Оператор цикла for

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

метка for (выражение1; выражение2; выражение3) {блок}

Первое выражение вычисляется перед началом цикла. Второе выражение вычисляется перед началом каждой итерации и, если оно оказывается ложью, выполнение цикла прерывается. Третье выражение вычисляется в конце каждой итерации. Метка, то есть идентификатор, заканчивающийся двоеточием, используется для передачи управления в начало цикла в случае, когда нормальное выполнение тела цикла прерывается (см. описание команд next, redo и last в последующих разделах).

Подсказка: Если при первом входе в цикл, проверяемое условие оказывается ложным, тело цикла не выполняется вообще.

Имеется множество путей использования оператора цикла for. Классический способ состоит в том, чтобы определить переменную цикла и изменять ее значение с заданным шагом, пока она не выйдет за некоторые границы. В следующем примере мы используем переменную цикла $loop_ind, чтобы пять раз напечатать строку "Hello!\n":

for ($loop_ind = 1; $loop_ind <= 5; $loop_ind++) {

   print "Hello!\n"

}

Hello!

Hello!

Hello!

Hello!

Hello!

Не возбраняется использовать несколько переменных цикла:

for ($loop_ind = 0, $double = 0.0, $text =’\n’;

     $loop_ind <= 4;

     $loop_ind++, $double = 2.0 * loop_ind) {

         print "Loop index " . $loop_ind .

               " doubled equals " . $double . $text;

}

Loop index 0 doubled equals 0

Loop index 1 doubled equals 2

Loop index 2 doubled equals 4

Loop index 3 doubled equals 6

Loop index 4 doubled equals 8

По завершении цикла можно проверить переменную-индекс, чтобы узнать, сколько было выполнено итераций:

$factorial = 1;

for ($ind_loop = 1; $ind_loop <= 6; $ind_loop++) {

   $factorial *= $ind_loop;

}

print $ind_loop-1 . "! = $\factorial\n";

6! = 720;

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

Если вы хотите, чтобы переменные цикла были недоступны вне его, используйте при их описании спецификатор my:

$factorial = 1;

for (my $ind_loop = 1; $ind_loop <= 6; $ind_loop++) {

   $factorial *= $ind_loop;

}

(см. также раздел «Управление областью видимости (ключевые слова my и local)» в главе 5).

На самом деле, при работе с for нет необходимости увеличивать индекс цикла и проверять его значение. В следующем примере из входного потока данных STDIN считываются и выводятся строки до тех пор, пока не встретится строка, начинающаяся с букв q или Q (что является сокращением для quit или QUIT). Обратите внимание, что входной текст необходимо присвоить промежуточной переменной $line, поскольку ввод из входного потока направляется по умолчанию в переменную $_ только в случае цикла while:

for ($line = <>; $line =~ /^q/i; ; $line = <> {

   print $line;

}

Оператор цикла foreach, рассматриваемый в следующем разделе, иногда используется идентично оператору цикла for (по сути дела в Perl они выполняют одни и те же действия). В следующем примере оператор цикла foreach полноценно работает с переменной-индексом цикла:

foreach ($loop_ind = 1; $loop_ind <= 5; $loop_ind++) {

   print "Hello!\n";

}

Hello!

Hello!

Hello!

Hello!

Hello!

Наконец, рассмотрим также пример, когда оператор for работает наподобие foreach:

@array = ("Hello ", "there.\n");

for (@array) {print;}

Hello there.

Оператор цикла foreach

В Perl оператор цикла foreach является синонимом for. Однако, когда требуется, чтобы переменная цикла последовательно перебирала данные из некоторого заданного списка значений, программисты зачастую используют в явном виде оператор foreach (чтобы подчеркнуть, что смысл цикла выражается фразой «for each element in ...»). Этот оператор цикла записывается как:

метка foreach переменная (список) {блок}

Во время итерации из списка извлекается очередное значение, присваивается переменной и выполняется тело цикла. Цикл завершается, когда список исчерпан. Как и раньше, метка используется для передачи управления в начало цикла в случае, когда нормальное выполнение очередной итерации прерывается (см. описание команд next, redo и last в последующих разделах).

Если в заголовке цикла не задано имя переменной, используется имя $_ (это удобно при работе с функциями, использующими $_ как аргумент по умолчанию — например, print):

@array = ("Hello ", "there.\n");

foreach (@array) {print;}

Hello there.

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

Оператор foreach можно использовать и для итерации по содержимому хеша, используя результат работы функции keys или values:

$hash{fruit} = orange;

$hash{sandwich} = clubburger;

$hash{drink} = lemonade;

foreach $key (keys %hash) {

   print $hash{$key} . "\n";

}

lemonade

clubburger

orange

У цикла foreach есть одна особенность: вместо того, чтобы присваивать переменной значение из списка, переменная становится синонимом этого значения. В частности, если очередным элементом списка является имя переменной, то изменение значения переменной цикла изменит и эту переменную. Если в качестве списка выступает переменная-массив, то изменения сказываются на ее содержимом:

@array = (1, 2, 3);

foreach $element (@array) {

   $element += 1;

}

print join(", ", @array);

2, 3, 4

Не следует изменять структуру списка, используемого как аргумент команды foreach, в процессе работы цикла (например, с помощью функции splice, примененной к массиву, указанному вместо списка) — в противном случае цикл, скоре всего, будет работать неправильно. Аналогично, не стоит использовать в качестве переменной цикла специальные переменные Perl или имена, связанные с другими объектами (с помощью функции tie или метода TIESCALAR, например).

Функция each позволяет организовывать работу с хешами в стиле, напоминающем выполнение цикла foreach. Она последовательно возвращает пары ключ/значение, хранящиеся в хеше, как показано в следующем примере:

$hash{fruit} = orange;

$hash{sandwich} = clubburger;

$hash{drink} = lemonade;

while (($key, $value) = each(%hash)) {

   print "$key => $value\n";

}

drink => lemonade

fruit => orange

sandwich => clubburger

Оператор цикла while

Оператор while играет важную роль в Perl. Вот как он выглядит:

метка while (выражение) {блок}

метка while (выражение) {блок} continue {блок’}

Тело цикла выполняется, пока выражение в заголовке цикла остается истинным (перед каждым выполнении тела цикла оно вычисляется повторно). Метка используется для передачи управления в начало цикла в случае, когда нормальное выполнение тела цикла прерывается (см. описание команд next, redo и last в последующих разделах).

Следующий пример суммирует доходы пользователя до тех пор, пока сумма не превысит миллион<$FВообще говоря, для работоспособности данного примера необходимо удалять конец строки после ввода значения с терминала ($_ = <>; chomp $_;). Кроме того, постоянное использование автором chop вместо chomp — тоже дурной стиль. — Примеч. ред.>:

$savings = 0;

while ($savings < 1_000_000) {

   print "Enter the amount you earned today: ";

   $savings +- <>;

}

print "Congratulations, millionaire.\n";

А в этом примере (он используется также в предыдущем разделе) цикл while применяется для перебора всех значений хеша с помощью функции each, возвращающей при каждом обращении очередную пару ключ/значение:

$hash{fruit} = orange;

$hash{sandwich} = clubburger;

$hash{drink} = lemonade;

while (($key, $value) = each(%hash)) {

   print "$key => $value\n";

}

drink => lemonade

fruit => orange

sandwich => clubburger

Специальная форма цикла — цикл while (<>) — имеет полезное свойство: внутренняя переменная Perl $_ автоматически заполняется данными, построчно вводимыми через стандартный поток ввода. Это означает, что можно с пользой применять многочисленные функции Perl, использующие переменную $_ как аргумент по умолчанию:

while (<>) {

   print;

}

Блок continue, если он задан, выполняется всякий раз, когда тело цикла выполнено полностью или частично (см. далее раздел «Команда next: как перейти к следующей итерации цикла») перед очередной проверкой условия цикла. Например, с помощью блока continue, можно заставить цикл while вести себя наподобие for:

$loop_index = 1;

while ($loop_index <= 5) {

   print "Hello!\n";

} continue {

   $loop_index++;

}

Hello!

Hello!

Hello!

Hello!

Hello!

Оператор while проверяет условие перед выполнением тела цикла, так что оно может вообще ни разу не выполниться. Это удобно, если тело цикла организовано таким образом, что его выполнение при нарушенном условии цикла может вызывать проблемы. Например, в следующем примере программа не будет печатать строки, вводимые из файла, если дескриптор файла FileHandle не связан ни с одним файлом:

while (<FileHandle>) {

   print;

}

Оператор цикла until

Оператор until выполняет те же функции, что и while, за тем исключением, что для выполнения тела цикла требуется, чтобы проверяемое условие было ложью. Этот цикл записывается как:

метка until (выражение) {блок}

метка until (выражение) {блок} continue {блок’}

По аналогии с примером из предыдущего раздела, напечатаем пять раз подряд с помощью оператора цикла until текст "Hello!\n":

$loop_index = 1;

until ($loop_index > 5) {

   print "Hello!\n";

} continue {

   $loop_index++;

}

Hello!

Hello!

Hello!

Hello!

Hello!

Модификаторы if, unless, until,while и for

Кроме формальных конструкций условных операторов и операторов цикла Perl позволяет добавлять модификаторы с аналогичными функциями в конец любой стандартной команды:

if выражение

unless выражение

while выражение

until выражение

for (список)

foreach (список)

Работа этих модификаторов в значительной степени повторяет работу стандартных операторов цикла и условного оператора, но они зачастую облегчают чтение кода. Например, вот как печатается сообщение "Too big!\n" с помощью модификатора if, если пользователь вводит число больше ста:

while (<>) {

   print "Too big!\n" if $_ > 100;

}

В следующем примере модификатор unless используется, чтобы вывести сообщение об ошибке и выйти из программы, если открыть файл невозможно:

die "Cannot open the file.\n" unless open($filename);

Точно так же, например, для печати входного потока вместо цикла while с телом, состоящим из одной команды print, можно использовать команду print с модификатором while:

print while (<>);

Модификаторы for и foreach (которые отличаются друг от друга только записью, но не действием) требуют дополнительного разъяснения. В отличие от операторов for и foreach, модификаторы for и foreach не позволяют задавать переменную-индекс — для этой цели всегда используется специальная переменная $_, подразумеваемая по умолчанию, а в качестве условия, определяющего итерации, служит список значений, а не тройка команд. Во всем остальном работа модификаторов не отличается от работы операторов цикла:

print $_ foeach (1, 2, 3, 4, 5, 6, 7, 8);

12345678

Подсказка: Особенности работы модификаторов while и until с командой do рассматривваются в следующем разделе.

Как создать цикл do while

Многие программисты думают, что если в языке программирования есть цикл while, то должен быть и цикл do while. Однако в Perl\ это не так. Точнее, нет отдельного цикла do while, но есть команда do, которая записывается как:

do {блок}

do подпрограмма (параметры)      # Не рекомендуется

do выражение

Команда do {блок команд} выполняет указанную последовательность команд и возвращает значение, соответствующее последней выполненной команде. Команда do подпрограмма (параметры) выполняет вызов подпрограммы, и ее рекомендуется заменять на более стандартную команду call (или оформлять вызов подпрограммы в виде блока). Команда do выражение интерпретирует выражение как имя файла (например, do "myscript.pl") и выполняет поток команд, содержащихся в этом файле.

Если использовать команду do с модификатором while, то мы получим конструкцию, имитирующую поведение цикла do while:

do {

   print;

} while (<>);

Необходимо отметить, что команда do с модификатором while будет выполнена по крайней мере один раз, то есть даже в том случае, если условие оказывается ложью с самого начала (это — один из примеров чувствительности конструкций Perl к контексту, в котором они используются). Например, в отличие от команды «print "ABC" while (0);», которая не будет выполнена ни разу, команда «do {print "ABC"} while (0);» напечатает текст.

Подсказка: Точно таким же образом совместно с оператором do работает и модификатор until. А именно, команда «do {print "ABC"} until (1);» напечатает текст "ABC", хотя и один раз.

Однако необходимо подчеркнуть, что такие конструкции, тем не менее, не являются настоящими операторами цикла. В частности, команды next, redo и last, управляющие выполнением цикла и описанные в следующих разделах, не будут работать в случае команды do с модификатором while или until.

Команда next: как перейти к следующей итерации

Команда next, указанная внутри цикла, позволяет немедленно начать следующую итерацию, пропуская команды, которые могут находиться после нее в теле блока. Как и положено, перед началом итерации проверяется условие, указанное в заголовке цикла (циклы while, until и for), изменен индекс цикла (цикл for), сделана выборка следующего элемента списка (forerch), и т. д.. Если команда next передает управление следующей итерации цикла while или until, для которого имеется раздел команд continue, то блок команд continue будет выполнен до очередной проверки условия цикла и следующей итерации.

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

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

NUMBER: while (<>) {

   next NUMBER if /^-/;

   print;

}

В этом примере, если строка начинается с минуса, мы переходим к следующей итерации, минуя команду print.

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

OUTER: for ($outer = 0; $outer < 10; $outer++) {

            $result = 0;

INNER:      for ($inner = 0; $inner < 10; $inner++) {

                   $result += $inner * $outer;

                   next OUTER if $inner == $outer;

                   print "$result\n";

            }

       }

Команда last: как прервать выполнение цикла

Команда last немедленно прекращает выполнение цикла (подобно команде break языка C). Если есть блок команд continue, то он также не выполняется.

В следующем примере цикл while используется для удаления из файла начальных комментариев, причем команда last прерывает цикл, если строка начинается не с символа #:

# Strip this line

# Strip this line too

COMMENTS: while (<>) {

   last COMMENTS if !/^#/;

}

do {

   print;

} while (<>);

Если запустить эту программу, подав в качестве входного файла ее собственный текст (например, набрав в командной строке «perl strip.pl strip.pl»), то на выходе получим:

COMMENTS: while (<>) {

   last COMMENTS if !/^#/;

}

do {

   print;

} while (<>);

Команда redo: как вернуться к началу итерации

Команда redo начинает текущую итерацию без проверки условия, указанного в заголовке цикла (циклы while, until и for), без приращения переменной цикла (цикл for), без выборки из списка очередного значения (цикл foreach), и т. д.. Выполнение цикла продолжается, так, как если бы текущей итерации еще не было (хотя, естественно, все изменения значений переменных, сделанных во время текущей итерации, сохраняются). Если у цикла имеется раздел continue (циклы while или until), указанные в нем команды также пропускаются.

Предположим, что у нас имеется сценарий Perl (файл с именем code.pl), в котором в качестве символа продолжения строки используется знак подчеркивания (что запрещено синтаксисом Perl):

for ($loopindex = 0; _

     $loopindex <= 10; _

     $loopindex++) { _

     print $loopindex; }

Чтобы прочитать файл code.pl, объединить команды, разбитые на отдельные строки, и выполнить получившуюся программу с помощью команды Perl eval, можно использовать следующий сценарий:

while (<>) {

   if (s/_//g) {   # Match and remove underscores

      $_ .= <>;

      redo;

   }

   eval;

}

Если этот сценарий поместить в файл evaluate и выполнить с его помощью код, содержащийся в файле code.pl, то мы получим:

%evaluate code.pl

012345678910

Подсказка: Более подробно о команде eval рассказывается далее в этой главе в разделе «Выполнение кода Perl с помощью команды eval».

Блок команд как оператор цикла

Хотя отдельный блок команд формально и не является циклом, в теле блока можно использовать те же самые команды next, redo и last, которые управляют и выполнением циклов. Синтаксис Perl допускает конструкции вида

метка {блок}

метка {блок} continue {блок’}

где идентификатор-метка (заканчивающийся двоеточием) может использоваться командами передачи управления next, redo, last и даже командой goto.

Если для блока указана конструкция continue, то, как и в случае циклов while и until, ассоциированный с нею набор команд выполнится даже в том случае, когда нормальное выполнение тела блока прервано с помощью команды next. Если же выполнение блока прерывается командами redo или last, раздел continue будет пропущен. Команда redo, указанная в теле блока или разделе continue, передает управление в начало блока, команда last обеспечивает выход из блока, минуя раздел continue, команда next передает управление разделу continue с последующим выходом из блока (если только в этом разделе нет других команд передачи управления).

Пример (имитация цикла for):

$index = 1;

START: {

   if ($index > 5) {last START};

   print "Hello";

   if ($index == 5) {next START};

   print "... ";

} continue {

   $loop_index++;

   redo START;

}

print "!\n";

Hello... Hello... Hello... Hello... Hello.! 

Если после команд next, redo или last задан идентификатор-метка (без двоеточия), то эта команда передачи управления относится к блоку или циклу, чья метка указана. Если передача управления относится к ближайшему блоку, внутри которого находится команда, метку указывать необязательно.

Создание переключателей switch

Операторы switch сравнивают проверяемое значение с набором других значений, и в зависимости от результатов сравнения выполняют тот или иной фрагмент кода. В Perl нет встроенного оператора switch<$Fвероятно, такой оператор появится в следующей версии Perl. — Примеч. ред.>, но вы можете создать его заменитель, используя другие конструкции Perl.

Например, для имитации оператора switch можно использовать блоки кода:

while (<>) {

   SWITCH: {

      /run/ && do {

         $message = "Running\n";

         last SWITCH;

      };

      /stop/ && do {

         $message = "Stopped\n";

         last SWITCH;

      };

      /connect/ && do {

         $message = "Connected\n";

         last SWITCH;

      };

      /find/ && do {

         $message = "Found\n";

         last SWITCH;

      };

      DEFAULT:

         $message = "No match.\n";

   }

}

Здесь используется свойство «быстрой работы» оператора &&: если первый операнд оказывается ложью, второй не вычисляется. Для сравнения переменной $_ с различными вариантами значений ("run", "stop", "connect" и "find"), используется сравнение по шаблону. Поскольку в Perl блок кода представляет собой цикл, исполняемый один раз, команда last обеспечивает выход из блока. Если пользователь введет одно из слов "run", "stop", "connect" или "find", будет выведено соответствующее сообщение.

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

В документации, прилагающейся к Perl, вы найдете и другие примеры конструкций, замещающих оператор switch. (Как правило, однако, наиболее «прозрачной» конструкцией для имитации работы оператора switch будет использование условного оператора if–elsif–...–elsif–else.)

Оператор безусловной передачи управления goto

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

Имеется три варианта команды goto:

goto метка

goto выражение

goto &подпрограмма

В первом случае управление передается команде, перед которой расположена соответствующая метка (то есть идентификатор, «помечающий» определенную команду программы и отделенный от этой команды двоеточием). Если задано выражение, то результатом его вычисления должна быть одна из существующих меток. Наконец, последняя форма оператора goto используется для подпрограмм.

Рассмотрим более подробно, как метки работают. В большинстве случаев в Perl метка — это на самом деле не метка, а имя оператора цикла, поскольку блок может расссматриваться как однократно выполняемый цикл. Оператор goto находит такую метку и передает управление в начало соответствующего оператора или блока операторов. Не разрешается передавать управление внутрь конструкций, требующих инициализации (подпрограммы, операторы цикла, откомпилированные внешние модули, и т. д.). Однако, например, можно использовать оператор goto для передачи управления изнутри подпрограммы внешнему блоку (хотя, естественно, лучше этого не делать). В следующем примере оператор goto используется для создания бесконечного цикл, читающего вводимые значения до тех пор, пока пользователь не наберет слово «exit»:

INPUT: $line = <>;

if ($line !~ /exit/) {print "Try again\n"; goto INPUT}

Вторая форма оператора goto подразумевает в качестве аргумента имя метки (текстовое выражение), вычисляемое в процессе выполнения программы. В частности, с его помощью можно имитировать индексируемый оператор goto языка FORTRAN:

goto ("LABEL1", "LABEL2", "LBL_FINAL") [$index];

Наконец, третья форма goto представляет собой некий магический трюк и при обычных обстоятельствах вряд ли будет задействована. При вызове команды goto &подпрограмма происходит передача управления соответствующей подпрограмме, однако массив @_, содержащий ее аргументы, не формируется (см. главу 7 относительно подпрограмм Perl) и адрес возврата не запоминается. Тем самым этот оператор может использоваться только внутри подпрограммы, чтобы скрытым (для точки вызова) образом передать управление другой, изначально не вызываемой пользователем подпрограмме. Все модификации массива @_ сохраняются при передаче управления новой подпрограмме. При выходе из подпрограммы, указанной в операторе goto, управление возвращается точке вызова первой подпрограммы.

Стоит подчеркнуть, что в подавляющем большинстве случаев следует использовать структурные операторы управления ходом программы Perl (условный оператор if-then-else, операторы цикла for, foreach и while, операторы next, last и redo, перехват исключительных ситуаций с помощью пары команд eval {} и die (), условные модификаторы команд и т. д.). Они специально оптимизированы для работы интерпретатора Perl и, помимо улучшения структуры программы, заметно влияют на скорость выполнения.

Выполнение кода Perl с помощью команды eval

Чтобы выполнить фрагмент кода Perl, используется команда eval:

eval выражение

eval {блок}

Если аргумент не задан, используется выражение, содержащееся в переменной Perl $_.

Например, так выполним команду «print "Hello\n"»:

eval "print \"Hello\n\"";

Hello

(здесь внутренние двойные кавычки заданы, естественно, через escape-последовательности).

Вы можете выполнить за один раз несколько команд, или, даже, целый сценарий Perl:

eval "print \"Hello \"; print \"there\n\"";

Hello there

Вот как интерактивно выполняются команды Perl, если только они занимают не более одной строки:

while (<>) {eval;}

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

Выход из программы с помощью команды exit

Команда exit завершает работу программы:

exit выражение

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

print "Please type the letter y\n";

while (<>) {

   if ($_ ne ’y’) {

      print "Please type the letter y\n";

   } else {

      print "Do you always do what you’re told?\n";

      exit;

   }

}

Выход из программы с помощью команды die

Команда die предназначена для выхода из программы в случае возникновения ошибок и других непредвиденных ситуаций. Она печатает в выходной поток STDERR список значений, указанный в качестве ее параметра, и завершает работу программы:

die список

Кроме остановки программы, команда die возвратит в качестве кода завершения текущее значение специальной переменной Perl $!. Если die выполняется с помощью eval, то сообщение об ошибке помещается в специальную переменную $@ (а не в STDERR), а вместо прекращения работы сценария прекращается лишь выполнение команды eval.

В следующем примере делается попытка открыть несуществующий файл:

$fname = "nonexist.pl";

open FileHandle, $fname or die "Cannot open $fname\n";

Этот сценарий завершается с сообщением об ошибке:

Cannot open nonexist.pl