Язык программирования Perl

Управляющие структуры

Показывать лекцию целиком

Термы и простые предложения

Минимальная синтаксическая единица языка программирования называется термом. Терм (term) - это все то, что может служить операндом в выражении, например, литерал или переменная. Выражение напоминает неоконченную фразу в естественном языке. Чтобы выражение стало законченным предложением (statement, называемым также утверждением), нужно после него поставить символ ";" (точка с запятой). Другими словами, простое предложение - это выражение, вычисляемое ради его побочного эффекта. Кроме предложений, в программе на Perl могут присутствовать объявления переменных и подпрограмм, которые будут рассмотрены позднее. Приведем примеры простых предложений:

$count = 0; # присваивание значения переменной
$count++;   # изменение значения операнда
$result = 'Итого=' . $count . "\n"; # подготовка к печати
1; # литерал - минимальное, но корректное предложение

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

Пробельные символы

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

$count=0;$count++;$result='Итого='.$count."\n";1;

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

Блоки и составные предложения

Простые предложения, составленные из выражений, выполняются одно за другим, образуя простую последовательность действий. Последовательность может помещаться в блок - одно или несколько предложений, обрамленных фигурными скобками, которые рассматриваются как единое целое. Блоки применяются для группировки программных конструкций, а также для задания области видимости переменных. Точка с запятой может не ставиться в конце последнего предложения в блоке (как это делается в языке Pascal), но лучше ее ставить всегда, на случай, если позднее добавится еще одно предложение. Блок предложений может быть частью управляющей конструкции, такой как цикл или условный оператор. А блок, который не входит ни в одну управляющую конструкцию, называется голым блоком (bare block).

{ # начало блока
   # последовательность предложений,
   # образующих тело блока
} # конец блока

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

Условия

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

if (условие) { # проверка истинности условия
   # действия, выполняемые, 
   # если условие истинно
}
# продолжение программы

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

if ($count % 2 == 1) { # остаток от деления на 2 равен 1
   print $count;
}

Другая общеизвестная форма условного предложения содержит блок, выполняемый при ложном условии, а именно:

if (условие) { # проверка истинности условия
   # действия, выполняемые, 
   # если условие истинно
} else {
   # действия, выполняемые в противном случае
   # (если условие ложно)
}
# продолжение программы

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

if (условие 1) { # проверка истинности 1-го условия
   # действия, выполняемые, 
   # если условие 1 истинно
} elsif (условие 2) { # проверка истинности 2-го условия
   # действия, выполняемые, 
   # если условие 2 истинно
   # ... здесь могут быть еще фразы elsif ...	
} elsif (условие N) { # проверка истинности N-го условия
   # действия, выполняемые, 
   # если условие N истинно
} else {
   # действия, выполняемые, 
   # если все условия ложны
}
# продолжение программы

При этом выполнится один из блоков действий: соответствующий первому истинному условию или указанный за фразой else. Фраза else может отсутствовать, тогда при всех ложных условиях не делается ничего. Форма условного предложения с elsif заменяет отсутствующий в Perl оператор выбора (такой как do-case или switch ).

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

unless (условие) { # проверка ложности условия
   # действия, выполняемые, 
   # если условие ложно
} else {
   # действия, выполняемые 
   # в противном случае
}
# продолжение программы

Здесь фраза else также может отсутствовать, если при истинности условия не требуется выполнять никаких действий.

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

($count % 2 == 1) and print $count;

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

Циклы

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

while (условие) { # проверка истинности условия продолжения
   # действия, выполняемые,
   # пока условие истинно 
}
# продолжение программы

Например, можно воспользоваться таким циклом while для вывода на системную консоль десяти случайных чисел от 0 до 1, сгенерированных встроенной функцией rand:

$i = 0;
while ($i < 10) {    # повторять, пока счетчик не достиг 10
   print "\n", rand; # вывести с новой строки случайное число
   $i++;             # увеличить счетчик повторений
}

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

until (условие) { # проверка истинности условия окончания
   # действия, выполняемые,
   # пока условие ложно 
}
# продолжение программы

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

$i = 0;
until ($i == 10) {  # закончить, когда счетчик достигнет 10
  print "\n", rand; # вывести с новой строки случайное число
  $i++;             # увеличить счетчик повторений
}

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

for (первое выражение; условие; второе выражение) {
   # действия, выполняемые,
   # пока условие истинно 
}
# продолжение программы

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

for ($i=0; $i<10; $i++) { # повторить 10 раз, увеличивая $i
   print "\n", rand; # вывести с новой строки случайное число
}

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

foreach переменная (список) { #
   # работа с переменной, содержащей
   # текущий элемент обрабатываемого списка
}

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

Управление циклами и переходы

В Perl есть несколько предложений для управления выполнением программы с помощью перехода в указанную точку программы. Обычно это требуется при работе с циклами. Когда при работе цикла требуется досрочно закончить его выполнение при наступлении какого-то события, то для этого можно воспользоваться оператором last (аналог оператора break в языке C), который прерывает выполнение цикла и переходит к выполнению предложения, следующего за циклом. Например, напечатать 10 случайных чисел от 0 до 0,5 можно так:

$i = 0;
while (1) {                # безусловно входим в цикл
   $random = rand;         # получаем случайное число
   if ($i == 10) {         # по достижении счетчика 10
      last;                # ПРЕРЫВАЕМ выполнение цикла
   }
   if ($random < 0.5) {    # числа меньше 0.5
      print "\n", $random; # выводим на консоль
      $i++;                # и увеличиваем счетчик повторений
   }
}
# сюда произойдет переход по last

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

$i = 0;
while ($i < 10) {       # пока счетчик не достигнет 10
   $random = rand;      # получаем случайное число
   if ($random > 0.5) { # числа больше 0.5
      next;             # ПРОПУСКАЕМ действия в теле цикла
   }
   print "\n", $random; # выводим число на консоль
   $i++;                # и увеличиваем счетчик повторений
   # сюда произойдет переход по next
}

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

$i = 0;
while ($i < 10) {       # пока счетчик не достигнет 10
   # сюда произойдет переход по redo
   $random = rand;      # получаем случайное число
   if ($random > 0.5) { # числа больше 0.5
      redo;             # СНОВА НАЧИНАЕМ действия в теле цикла
   }
   print "\n", $random; # выводим число на консоль
   $i++;                # и увеличиваем счетчик повторений
   }

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

# может быть заголовок цикла: while, until или for
{
   # сюда происходит переход по redo
   # действие 1, выполняемое в цикле
   if (условие выхода) {
      last; # выход из цикла
   }
   # действие 2, выполняемое в цикле
   if (условие продолжения) {
      next; # выполнение следующей итерации цикла
   }
   # действие 3, выполняемое в цикле
   if (условие возобновления) {
      redo; # выполнение тела цикла сначала
   }
   # действие N, выполняемое в цикле
   # сюда происходит переход по next
} continue {
   # действие, выполняемое после нормального завершения цикла
}
# сюда происходит переход по last

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

CYCLE_1:
while (условие продолжения цикла 1) {
   CYCLE_2:
   while (условие продолжения цикла 2) {
      if (условие выхода из всех циклов) {
         last CYCLE_1;
      }
      CYCLE3:
      while (условие продолжения цикла 3) {
         if (условия прерывания 2-го цикла) {
            next CYCLE_2;
         }
      }
   # сюда произойдет переход по next CYCLE_2
   }
}
# сюда произойдет переход по last CYCLE_1

Метка может ставиться перед любым предложением. При помощи блока и операторов управления циклом с меткой можно имитировать управляющую структуру, подобную оператору switch в языке C. Например, так можно записать только одно присваивание переменной $say в зависимости от условия:

SWITCH: {
   unless (defined $t) { # если $t не определено
      $t = 25; redo SWITCH; # задать значение по умолчанию
   }
   if ($t < 10) { $say = 'холодно'; last SWITCH; }
   if ($t < 18) { $say = 'прохладно'; last SWITCH; }
   if ($t < 27) { $say = 'тепло'; last SWITCH; }
   $say = 'жарко';
}
print "Сегодня $say\n";

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

  • Переход к выполнению предложения, перед которым стоит метка:
    goto МЕТКА;
  • Переход к метке, имя которой получено в результате вычисления выражения:
    goto выражение;
  • Замена вызова указанной подпрограммы на подпрограмму, выполняющуюся в данный момент (применяется загрузчиками модулей Perl):
    goto подпрограмма;
  • Оператор goto заслуженно порицается теоретиками и практиками программирования, поскольку он сильно запутывает логику выполнения программы. Так что правилами хорошего стиля программирования рекомендуется использовать его только при крайней необходимости. Хотя goto и можно применить для выхода из цикла, но для этого лучше воспользоваться оператором last.

    Модификаторы

    Порядок выполнения действий в простом предложении можно задавать с помощью модификаторов выражений. За любым выражением может стоять один из следующих модификаторов:

    выражение if выражение
    выражение unless выражение
    выражение while выражение
    выражение until выражение
    выражение foreach список

    Модификатор задает условие выполнения (в случае if или unless ) или повторения (в случае while, until или foreach ) выражения. Выражение модификатора вычисляется в первую очередь, хотя и стоит в конце конструкции. Хотя модификаторы похожи на условные и циклические управляющие конструкции, но они формируют простые предложения и поэтому не могут быть вложенными. Приведенную выше конструкцию выбора можно переписать с использованием условных модификаторов:

    SWITCH: {
       $t = -36, redo SWITCH unless defined $t;
       $say = 'холодно', last SWITCH if $t < 10;
       $say = 'прохладно', last SWITCH if $t < 18;
       $say = 'тепло', last SWITCH if $t < 27;
       $say = 'жарко';
    }

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

    ++$count, --$sum while (rand < 0.1); 
    $random = rand until $random > 0.7;

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

    Выражения с do и eval

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

    $result = do { $x=rand; $a=0; } # в $result будет присвоен 0

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

    SWITCH: {
       (defined $t) || do { $t = 15; redo SWITCH; };
       ($t < 10)  do { $say = 'холодно'; last SWITCH; };
       ($t < 18)  do { $say = 'прохладно'; last SWITCH; };
       ($t < 27)  do { $say = 'тепло'; last SWITCH; };
       $say = 'жарко';
    }

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

    do { $sum += rand; } until ($sum > 25);

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

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

    eval выражение # вычислить выражение как код на Perl
    eval блок # выполнить блок, перехватывая возможные ошибки

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

    $x = 0; $y = 5; # в выражении будет деление на 0
    $expression = "$y/$x"; # строка, содержащая код для выполнения
    $result = eval ($expression); # вычислить выражение
    if ($@ eq '') { # проверить специальную переменную на ошибки
       print "Выражение вычислено: $result"; 
    } else {
       print "Ошибка вычисления: $@"; 
    }

    Во второй форме блок предложений в конструкции eval, как и в конструкции do, становится выражением. Он компилируется обычным образом и выполняется во время работы программы, но возможные ошибки его выполнения также не приводят к аварийному завершению программы. Причину ошибки можно узнать из специальной переменной $@, а значением eval будет значение последнего предложения в блоке. Вот пример обработки ошибок в выражении eval:

    $result = eval { # выполнить блок
       $x = 0; $y = 5; 
       $z = $y / $x; # здесь будет деление на 0
    }; # завершаем предложение точкой с запятой
    unless ($@) { # проверить специальную переменную на ошибки
       print "Выражение вычислено: $result"; 
    } else {
       print "Ошибка вычисления: $@"; 
    }

    Прагмы

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

    use strict; # ограничить применение небезопасных конструкций
    use warnings; # выводить подробные предупреждения компилятора
    use diagnostics; # выводить подробную диагностику ошибок

    Дополнительная диагностика компилятора поможет избежать многих ошибок при выполнении программы. Обычно прагмы могут включаться в любом месте программы с помощью ключевого слова use и выключаться при необходимости с помощью ключевого слова no, например:

    use integer; # включить целочисленные вычисления
    print 10 / 3; # результат: 3
    no integer; # отключить целочисленные вычисления
    print 10 / 3; # результат: 3.33333333333333

    С помощью прагмы use constant можно определять в программе именованные константы, которые по традиции записываются заглавными буквами. Это делается таким образом:

    use constant PI => 3.141592653; # число пи

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

    use locale;

    По ходу изучения материала следующих лекций будут рассмотрены другие полезные прагмы, а в лекции 13 будет описано применение use для подключения внешних модулей.

    Синонимы и идиомы

    Материал этой лекции иллюстрирует упоминавшийся в лекции 1 принцип TIMTOWTDI: в Perl часто существует несколько синонимичных конструкций, предоставляющих автору программы возможность наиболее точно выразить свой замысел в привычном для него стиле. Perl - демократичный язык, и каждый пишет на нем так, как ему удобнее и привычнее: начинающий программист использует простые средства, писавший ранее на другом языке найдет для себя знакомые конструкции, а опытный Perl-хакер может углубиться в синтаксические дебри. За многие годы использования Perl целой армией программистов в нем сложились устойчивые выражения (idioms, идиомы), подобные пословицам и поговоркам в естественных языках. Для примера можно привести некоторые из них:

    # 1. Выполнять бесконечный цикл
    for (;;) # читается "forever" - "всегда"
       { }   # тело бесконечного цикла
    # 2. Открыть файл или аварийно завершить программу
    open FILE or die; # "открой файл или умри!"
    # 3. Читать строки из входного потока и печатать их,
    #    используя буферную переменную по умолчанию
    while (<>) { print; }
    # 4. Присвоить переменной значение по умолчанию
    #    только, если ее значение не определено
    $variable ||= $default_value;

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

    Оформление программы

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

    perldoc perlstyle

    В соответствии c устоявшимися традициями, типичная программа на языке Perl скорее всего будет выглядеть примерно так:

    # вводные комментарии к программе
    use strict;   # включение дополнительной...
    use warnings; # ... диагностики
    # use Env;      # подключение нужных модулей (см. лекцию 13)
    # package main; # объявление пакета (см. лекцию 13)
    
    my $message = 'Привет!'; # объявление переменных и
    print $message;          # запись алгоритма программы
    
    # описание форматов отчета (см. лекцию 10)
    # описание подпрограмм (см. лекцию 12)
    __END__       # необязательное обозначение конца программы

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

    perldoc perlsyn
    Вернуться к учебному плану