Глава 6.5. Операторы

6.5.1. Простые операторы

Простой оператор в PERLе — это выражение, которое может иметь единственный модификатор. Каждый простой оператор должен закачиваться точкой с запятой, если только он не является последним оператором в блоке; в этом случае точка с запятой может быть опущена. Существует пять модификаторов простых операторов:

Таблица 6.11. Модификаторы простых операторов
Синтаксис Описание
if expr Оператор выполняется, если выражение expr истинно.
unless expr Оператор выполняется, если выражение expr ложно.
while expr Оператор выполняется, пока выражение expr истинно.
until expr Оператор выполняется, пока выражение expr ложно.
foreach expr Оператор выполняется для каждого элемента списка expr, который перед этим присваивается переменной $_.

Примеры:

$count = 5;
print "$count\n" if $count;
print "$count\n" while $count--;

@people = qw/Анна Борис Виктор/;
print "$_\n" foreach @people;

Мы можем применять модификаторы не только к простым операторам, но и к блокам. Для этого перед блоком нужно поставить ключевое слово do:

do {
  $line = <STDIN>;
  ...
} until $line eq ".\n";

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

Конструкция do БЛОК (без модификатора) также используется в PERLе: она позволяет превратить блок в выражение и возвращает значение последнего оператора в этом блоке.

6.5.2. Составные операторы

Составные операторы состоят из блоков, заключенных в фигурные скобки. Напомним, что, в отличие от языков C или Java, фигурные скобки в составных операторах обязательны, даже если в них заключен только один оператор. PERL содержит следующие составные операторы:

  • условные операторы if и unless;
  • операторы цикла while, until и for;
  • оператор итерации foreach;
  • операторы управления циклом last, next и redo;
  • оператор перехода goto.

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

Условный оператор if позволяет проверить определенное условие и, в зависимости от его истинности, выполнить ту или иную последовательность операторов. Он имеет следующие формы:

if (выражение) БЛОК1
if (выражение) БЛОК1 else БЛОК2
if (выражение1) БЛОК1 elsif (выражение2) БЛОК2 ... else БЛОКn

Первая форма оператора означает, что если выражение истинно, то выполняется БЛОК1; если оно ложно, то управление передается оператору, следующему за if.

Вторая форма оператора означает, что если выражение истинно, то выполняется БЛОК1; если оно ложно, то выполняется БЛОК2.

Третья форма оператора означает, что если выражение истинно, то выполняется БЛОК1; если оно ложно и истинно выражение2, то выполняется БЛОК2 и т. д. Если ложны выражения во всех ветках оператора, то выполняется БЛОКn.

Следующий пример присваивает переменной $m наибольшее из трех чисел $x, $y, $z:

if ($x >= $y) {
  $m = ($x >= $z) ? $x : $z;
} else {
  $m = ($y >= $z) ? $y : $z;
}

6.5.2.2. Условный оператор unless

Условный оператор unless логически противоположен оператору if. Он имеет две формы:

unless (выражение) БЛОК1
unless (выражение) БЛОК1 else БЛОК2

Первая форма оператора означает, что если выражение ложно, то выполняется БЛОК1; если оно истинно, то управление передается оператору, следующему за unless.

Вторая форма оператора означает, что если выражение ложно, то выполняется БЛОК1; если оно истинно, то выполняется БЛОК2.

Предыдущий пример можно переписать так:

unless ($x < $y) {
  $m = ($x >= $z) ? $x : $z;
} else {
  $m = ($y >= $z) ? $y : $z;
}

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

Оператор цикла while имеет две формы:

while (выражение) БЛОК
while (выражение) БЛОК continue БЛОК1

Оператор while выполняется следующим образом:

  1. Вычисляется значение выражения. Если оно ложно, то управление передается оператору, следующему за данным оператором.
  2. Выполняется БЛОК.
  3. Если оператор содержит ключевое слово continue, то выполняется БЛОК1.
  4. Управление передается этапу 1.

Следующий пример выводит на экран числа от одного до десяти:

$i = 0;
while ($i++ < 10) { print "$i\n" }

Пользуясь второй формой оператора while, его можно записать так:

$i = 1;
while ($i <= 10) { print "$i\n" } continue { $i++ }

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

while (1) { print "Привет всем!" }

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

Оператор цикла until логически противоположен оператору while и также имеет две формы:

until (выражение) БЛОК
until (выражение) БЛОК continue БЛОК1

Оператор until выполняется следующим образом:

  1. Вычисляется значение выражения. Если оно истинно, то управление передается оператору, следующему за данным оператором.
  2. Выполняется БЛОК.
  3. Если оператор содержит ключевое слово continue, то выполняется БЛОК1.
  4. Управление передается этапу 1.

Следующий пример выводит на экран числа от одного до десяти:

$i = 1;
until ($i > 10) { print "$i\n" } continue { $i++ }

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

Оператор цикла for имеет вид:

for (инициализация; условие; изменение) БЛОК

Оператор for выполняется следующим образом:

  1. Выполняется оператор инициализация (обычно он инициализирует счетчик или счетчики цикла).
  2. Вычисляется значение выражения условие. Если оно ложно, то управление передается оператору, следующему за данным оператором.
  3. Выполняется БЛОК.
  4. Выполняется оператор изменение (обычно он увеличивает или уменьшает счетчик или счетчики цикла) и управление передается этапу 2.

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

for ($i = 1; $i <= 10; $i++) { print "$i\n" }

6.5.2.6. Оператор итерации foreach

Оператор foreach выполняет заданные действия для каждого элемента списка или массива. Он имеет две формы:

foreach переменная (список) БЛОК
foreach переменная (список) БЛОК continue БЛОК1

Оператор foreach выполняется следующим образом:

  1. Переменной присваивается очередной элемента списка. Если список исчерпан, то управление передается оператору, следующему за данным.
  2. Выполняется БЛОК.
  3. Если оператор содержит ключевое слово continue, то выполняется БЛОК1.
  4. Управление передается этапу 1.

Приведем все тот же пример с печатью чисел от 1 до 10:

foreach $i (1..10) { print "$i\n" }

Переменная является локальной для данного оператора, т. е. после завершения итерации ее предыдущее значение восстанавливается. Если перед переменной поставить ключевое слово my, то она будет лексически ограничена телом оператора. Если переменная опущена, то используется специальная переменная $_:

foreach (1..10) { print "$_\n" }

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

В действительности, операторы for и foreach являются синонимами, и вы можете использовать любое из этих слов по своему усмотрению.

6.5.2.7. Метки операторов

Перед операторами while, until, for и foreach, а также перед блоками могут ставиться метки. Помеченный оператор имеет вид:

метка: оператор

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

Хотя в современном программировании использование меток считается дурным тоном, в некоторых случаях их применение существенно упрощает логику программы. Чаще всего это происходит, когда метки используются для передачи управления из глубоко вложенного цикла к внешнему. Рассмотрим следующий пример, написанный в лучший традициях C++:

for (my $i = 0; $i < @ary1; $i++) {
  for (my $j = 0; $j < @ary2; $j++) {
    if ($ary1[$i] > $ary2[$j]) {
      last;
    }
    $ary1[$i] += $ary2[$j];
  }
}

Метки и операторы управления циклом позволяют записать этот алгоритм намного короче и, главное, намного прозрачнее:

OUTER:
for my $x (@ary1) {
  for my $y (@ary2) {
    next OUTER if $x > $y;
    $x += $y;
  }
}

6.5.2.8. Оператор last

Оператор last немедленно завершает указанный цикл. Он имеет две формы:

last метка
last

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

LINE: while (<STDIN>) {
  last LINE if /^$/; # прервать цикл, если встретилась пустая строка
  ...
}

6.5.2.9. Оператор next

Оператор next начинает новую итерацию указанного цикла. Он имеет две формы:

next метка
next

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

LINE: while (<STDIN>) {
  next LINE if /^#/; # пропускать комментарии
  ...
}

6.5.2.10. Оператор redo

Оператор redo начинает новую итерацию указанного цикла без проверки условия его выполнения. Он имеет две формы:

redo метка
redo

Первая форма оператора начинает новую итерацию цикла с заданной меткой. Вторая форма начинает новую итерацию самого внутреннего из выполняющихся в данный момент вложенных циклов. Если цикл имеет блок continue, то он не выполняется. Следующий пример удаляет комментарии из программы на языке Pascal, где они имеют вид {…} (пример упрощен, т. к. не учитывает, что символы {} могут содержаться в строковых константах):

LINE: while (<STDIN>) {
        while (s|({.*}.*){.*}|$1 |) {}
        s|{.*}| |;
        if (s|{.*| |) {
          $front = $_;
          while (<STDIN>) {
            if (/}/) {
              s|^|$front\{|;
              redo LINE;
            }
          }
        }
        print;
      }

6.5.2.11. Блок как вырожденный цикл

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

БЛОК1 continue БЛОК2

которая обычно означает, что выполняется БЛОК1, а после него БЛОК2. Однако, трактовка блоков как циклов означает и то, что мы можем пользоваться внутри блоков операторами управления циклом, и в этом случае указанная конструкция становится очень полезной.

Одна из причин, по которым в PERL не включен оператор выбора switch, состоит в том, что очень легко моделируется с помощью блоков и операторов управления циклом, например:

SWITCH: {
  $abc = 1, last SWITCH  if /^abc/;
  $def = 1, last SWITCH  if /^def/;
  $xyz = 1, last SWITCH  if /^xyz/;
  $nothing = 1;
}

6.5.2.12. Оператор перехода goto

PERL содержит оператор перехода goto трех видов:

goto метка
goto выражение
goto &имя

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

Вторая форма — это "вычисляемый goto": он вычисляет значение выражения и передает управление на полученную в результате метку, например:

goto ("LABEL1", "LABEL2", "LABEL3")[$i];

Третья форма — это вообще не goto в обычном смысле слова. Этот оператор подставляет вызов подпрограммы имя вместо выполняющейся сейчас подпрограммы. Она используется подпрограммами AUTOLOAD(), которые хотят незаметно подменить вызов одной подпрограммы другой. Имя не обязано быть именем подпрограммы; это может быть скалярная переменная или блок, значением которых является ссылка на подпрограмму.

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