07 апреля 2020

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

    Составные операторы — это второй тип операторов языка Perl. С их помощью реализуют ветвления в программе и организуют циклические вычисления. Эти операторы, в отличие от аналогичных операторов других языков программирования, определяются в терминах блоков — специальном понятии языка Perl, задающим область видимости переменных. Именно с блоков мы и начнем изучение составных операторов.
Блоки
     Блок — последовательность операторов, определяющая область видимости переменных. В программе блок обычно ограничен фигурными скобками {...}. Определяя синтаксис составных операторов, мы будем иметь в виду именно такой блок — последовательность операторов в фигурных скобках и обозначать его БЛОК. Интерпретатор рассматривает БЛОК как один оператор, вычисляемым значением которого является значение последнего выполненного оператора блока. Это означает, что там, где можно использовать один оператор, можно использовать и БЛОК. Такая ситуация встречается при использовании функции тар(). Она выполняет определяемый ее первым параметром оператор для всех элементов списка, заданного вторым параметром. Значение каждого элемента списка при вычислениях временно присваивается встроенной переменной $_. Возвращает эта функция список вычисленных значений оператора:
@rez = map $_ **= 2, @array; # Список квадратов элементов массива.

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

@rez = map { ++$kol; $_ **= 2} @array; # Список квадратов элементов
# массива и подсчет их количества.

      Обратите внимание, что возвращаемым значением блока операторов в этом примере является значение последнего оператора блока, которое и попадает в возвращаемый функцией тар () список.

Замечание
Блоки в Perl не ограничиваются только последовательностью операторов в фигурных скобках. Иногда блок может быть ограничен содержащим его файлом. Например, файлом, содержащим используемый в программе модуль Perl, или файлом самой программы.
     Как сказано в начале этого раздела, блок определяет область видимости переменных. Это означает, что в блоке можно создать переменные, обращаться к которым можно только из операторов, расположенных в этом блоке. Пока мы в блоке, мы можем присваивать им новые значения, использовать в вычислениях и т. п., но как только мы вышли из блока, мы теряем с ними "связь", они становятся "не видимыми". Такие переменные еще называют локальными переменными.

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

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

      Локальные переменные удобны для создания временных переменных, которые нигде больше не будут использоваться в программе, а только в одном определенном месте. Например, при отладке части кода часто приходится создавать временные переменные и выводить на печать их значения. Локальные переменные могут иметь такие же имена, как и глобально используемые переменные. Это не приводит к конфликту. После завершения операторов блока значение глобальной переменной имеет то же значение, которое она имела до начала выполнения операторов блока (пример 5.4).

# peri -w
$var = "outer"; # Глобальная переменная $var $glob = "glob"; # Глобальная переменная $glob my $lex = "outer_l"; # Лексическая переменная $1ех {
my($var) = "inner"; # Внутренняя переменная $var my($varl) = "inner_l"; # Внутренняя переменная $varl print "В блоке \$var = $var\n"; # Напечатает inner print "В блоке \$varl = $varl\n"; # Напечатает inner_l print "В блоке \$lex = $lex\n"; # Напечатает outer_l print "В блоке \$glob = $glob\n"; # Напечатает glob }
print "Вне блока \$var = $var\n"; # Напечатает outer print "Вне блока \$1ех = $lex\n"; # Напечатает outer_l print "Вне блока \$varl = $varl\n"; # Напечатает пустую строку ""

      Программа примера 5.4 демонстрирует области видимости лексических переменных. Внутри блока {...} "видны" переменные, созданные вне блока: и глобальные, и лексические ($giob, $iex), если только они не переопределены внутри блока ($var). При выходе из внутреннего блока восстанавливаются значения переменных, которые были переопределены внутри блока ($var). Доступ к локальным переменным блока извне невозможен ($vari).

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

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

В языке существует три формы оператора ветвления if:
if (ВЫРАЖЕНИЕ) БЛОК
if (ВЫРАЖЕНИЕ) БЛОК1 else БЛОК2
if (ВЫРАЖЕНИЕ!) БЛОК1 elsif (ВЫРАЖЕНИЕ2) БЛОК2 ... else БЛОКп

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

      Первый оператор if реализует простейшее ветвление. Если ВЫРАЖЕНИЕ истинно, то выполняются операторы из БЛОК, в противном случае БЛОК просто пропускается:

$var = 10;
if ( $var -= 5 ) {
print "Переменная \$var - $var"; }

      Обратите внимание, что в этом примере ВЫРАЖЕНИЕ представляет операцию составного присваивания. Это может показаться необычным, так как в большинстве языков программирования здесь требуется выражение, возвращающее булево значение. В Perl можно использовать любое выражение, в том числе и присваивание. Результат его вычисления интерпретируется в булевом контексте: если вычисленное значение равно о или пустой строке "", то оно трактуется как Ложь, иначе — Истина. Возвращаемым значением операции присваивания является значение, присвоенное переменной левого операнда. В нашем примере это число 5, следовательно в булевом контексте оно трактуется как Истина, а поэтому оператор печати print будет выполнен. Если перед выполнением оператора if переменная $var будет равняться 5, то выражение условия будет вычислено равным Ложь и все операторы блока будут просто пропущены.
     Обычно выражение условия представляет собой сложное выражение, составленное из операций отношения, связанных логическими операциями. Использование операции присваивания в выражении условия оператора if не совсем типично. Здесь мы его использовали, чтобы подчеркнуть то обстоятельство, что в Perl любое правильное выражение может быть использовано в качестве выражения условия, которое вычисляется в булевом контексте. Вторая форма оператора if используется, когда необходимо выполнить одну группу операторов (БЛОК!) в случае истинности некоторого выражения (ВЫРАЖЕНИЕ), а в случае его ложности — другую группу операторов (влок2):

if ($var >= 0} # ВЫРАЖЕНИЕ {
print "Переменная неотрицательна."; # БЛОК1, если ВЫРАЖЕНИЕ истинно } else {
print "Переменная отрицательна."; # БЛОК2, если ВЫРАЖЕНИЕ ложно }

По существу, первая форма оператора if эквивалентна второй форме, если БЛОК2 не содержит ни одного оператора.

Последняя, третья форма оператора if реализует цепочку ветвлений. Семантика этого оператора такова. Выполняются операторы из БЛОК!, если
ИСТИННО ВЫРАЖЕНИЕ!. ЕСЛИ ОНО ЛОЖНО, ТО ВЫПОЛНЯЮТСЯ Операторы ИЗ БЛОК2
в случае истинности выражение2. Если и оно ложно, то проверяется ВЫРАЖЕНИЕ З и т. д. Если ни одно из выражений условия оператора if не истинно, то выполняются операторы блока, определяемого после ключевого слова else в случае его наличия. В противном случае выполняется следующий после оператора if оператор программы. При выполнении следующего оператора ветвления if

if( $var < 0) { # ВЫРАЖЕНИЕ!
print "Переменная отрицательна"; i БЛОК! } elsif ( $var == 0) { # ВЫРАЖЕНИЕ2
print "Переменная равна нулю"; # БЛОК2 } else {
print "Переменная положительна"; # БЛОКЗ }

сначала проверяется условие отрицательности переменной $var. Если значение переменной строго меньше нуля (ВЫРАЖЕНИЕ1), то печатается сообщение из БЛОК! и оператор завершает свою работу. Если значение переменной не меньше нуля, то оно проверяется на равенство (ВЫРАЖЕНИЕ2) и в случае истинности выполняется оператор печати из блока операторов elsif (влок2). Если проверка на равенство нулю дала ложный результат, то выполняется оператор печати из блока операторов else (БЛОКЗ).

Замечание
Ключевое слово else вместе со своим блоком операторов может быть опущено.
     В операторе if со множественными проверками может быть сколько угодно блоков elsif, но только один блок else.
     Так как все операторы ветвления определяются в терминах блоков операторов, то не возникает двусмысленности при определении, какие операторы в какой части выполняются.
     При работе с операторами ветвления важно помнить, что только один блок операторов будет выполнен — тот, для которого истинно соответствующее выражение условия.
      Во всех операторах ветвления ключевое слово if может быть заменено на unless. В этом случае проверка выражения условия осуществляется на его ложность. Последний оператор if можно записать и так:

unless( $var >= 0} { # ВЫРАЖЕНИЕ!
print "Переменная отрицательна"; # БЛОК! } elsif ( $var == 0) { # ВЫРАЖЕНИЕ2
print "Переменная равна нулю"; # БЛОК2 } else {
print "Переменная положительна"; # БЛОКЗ }
При этом нам пришлось заменить ВЫРАЖЕНИЕ! на противоположное по смыслу.

Замечание
Все операторы if (unless) могут быть вложенными, т. е. в любом их блоке можно свободно использовать другие операторы ветвления.