Глава 4. Управляющие структуры
В этой главе:
Блоки операторов
Блок операторов — это последовательность операторов, заключенная в парные
фигурные скобки. Блок операторов выглядит следующим образом:
{
первыи_оператор;
второй_оператор;
третий_оператор;
...
последний_оператор;
}
Perl выполняет операторы по очереди, начиная с первого и кончая последним.
(Позднее вы узнаете о том, как можно изменять порядок выполнения в блоке, но пока
достаточно и этого.)
Синтаксически блок операторов принимается вместо любого одиночного оператора,
но обратное не верно.
Завершающая точка с запятой, стоящая за последним оператором, не обязательна.
Таким образом, вы можете разговаривать на языке Perl с С-акцентом (точка с запятой
присутствует) или с паскалевским акцентом (точка с запятой отсутствует).
Чтобы облегчить последующее добавление операторов, мы обычно рекомендуем
опускать точку с запятой лишь в том случае, если блок занимает целую строку.
Сравните примеры этих стилей в следующих двух блоках if:
if ($ready) { $hungry++ }
if ($tired)
{
$sleepy = ($hungry + 1) * 2;
}
Оператор if/unless
Следующей по сложности управляющей структурой является оператор if. Эта конструкция
состоит из управляющего выражения (проверяемого на истинность) и блока. В ней также
может быть часть, начинающаяся оператором else, за которой следует еще один блок операторов.
Другими словами, все это выглядит так:
if (выражение)
{
оператор_1;
оператор_2;
оператор_3;
}
else
{
оператор_1;
оператор_2;
оператор_3;
}
(Если вы любите программировать на С и Java, то для вас должна быть вполне очевидной
обязательность фигурных скобок. Они устраняют необходимость в применении правила
"путающего зависшего else".)
Во время выполнения Perl-программы вычисляется управляющее выражение. Если
оно истинно, то выполняется первый блок операторов в приведенном выше примере.
Если выражение ложно, то выполняется второй блок.
Что же такое "истина" и "ложь"? В Perl эти правила несколько странноваты, но,
тем не менее, дают ожидаемые результаты. Управляющее выражение вычисляется как
строковая величина в скалярном контексте (если это уже строка, ничего не изменяется,
а если это число, то оно преобразуется в строку (*1)). Если данная строка либо
пуста (т.е. имеет нулевую длину), либо состоит из одного символа "0" (цифры нуль), то
значение выражения — "ложь". Все остальное автоматически дает значение "истина".
Почему же в Perl столь забавные правила? А потому, что это облегчает условный переход
не только по нулю (в противоположность ненулевому числу), но и по пустой (в
противоположность непустой) строке, причем без необходимости создания двух версий
интерпретируемых значений "истина" и "ложь". Вот несколько примеров интерпретации этих
значений.
0 # преобразуется в "0", поэтому "ложь"
1-1 # дает в результате 0, затем
# преобразуется в "0", поэтому "ложь"
1 # преобразуется в "1", поэтому "истина"
"" # пустая строка, поэтому "ложь"
"1" # не "" или "0", поэтому "истина"
"00" # то-же, но будьте внимательны!!!
"0.000" # то-же, но будьте внимательны!!!
undef # дает в результате "", поэтому "ложь"
Таким образом, интерпретация значений как истинных или ложных достаточно интуитивна,
но пусть это вас не пугает. Вот пример полного оператора if:
print "how old are you?";
$a = <STDIN>;
chomp($a);
if ($a < 18)
{
print "So, you're not old enough to vote, eh?\n";
}
else
{
print "Old enough! Cool! So go vote!\n";
$voter++; # count the voters for later
}
Блок else можно опустить, оставив только часть, касающуюся then:
print "how old are you?";
$a = <STDIN>;
chomp($a);
if ($a < 18)
{
print "So, you're not old enough to vote, eh?\n";
}
Иногда бывает удобно часть "then" опустить и оставить только else, потому
что более естественно сказать "сделайте то, если это ложь", нежели "сделайте то,
если это — не истина". В Perl этот вопрос решается с помощью оператора unless:
print "how old are you?";
$a = <STDIN>;
chomp($a);
unless ($a < 18)
{
print "Old enough! Cool! So go vote!\n";
$voter++; # count the voters for later
}
Заменить if на unless — это все равно что сказать "Если управляющее выражение
ложно, сделать..." (Оператор unless может содержать блок else, как и оператор
if.)
Если у вас больше двух возможных вариантов, введите в оператор
if ветвь elsif, например:
if (выражение один)
{
оператор_1_при_истине_один;
...
оператор_n_при_истине_один;
}
elsif (выражение_два)
{
оператор_1_при_истине_два;
...
оператор_n_при_истине_два;
}
elsif (варажение_три)
{
оператор_1_при_истине_три;
...
оператор_n_при_истине_три;
}
else
{
оператор_1_при_всеи_ложных;
оператор_n_при_всех_ложных;
}
Все управляющие выражения вычисляются по очереди. Если какое-либо выражение
истинно, то выполняется соответствующая ветвь, а все остальные управляющие выражения
и соответствующие блоки операторов пропускаются. Если все выражения ложны, то выполняется
ветвь else (если таковая имеется). Присутствие блока else не обязательно, но он никогда
не помешает. В программе может быть столько ветвей elsif, сколько вам необходимо.
Оператор while/until
Ни один язык программирования не был бы полным без какой-нибудь формы организации
цикла (*2) (повторяющегося выполнения блока операторов). Perl может организовать
цикл с помощью оператора while:
while (выражение)
{
оператор_1;
...
оператор_n;
}
Чтобы выполнить оператор while, Perl вычисляет управляющее выражение (в данном
примере — выражение). Если полученное значение — "истина" (по принятым в Perl
правилам установления истинности), то один раз вычисляется тело оператора while.
Это повторяется до тех пор, пока управляющее выражение не станет ложным. Тогда Perl
переходит к оператору, следующему после цикла while. Например:
print "how old are you?";
$a = <STDIN>;
chomp($a);
while ($a > 0)
{
print "At one time, you were $a years old.\n";
$a --;
}
Иногда легче сказать "до тех пор, пока что-то не станет истинным", чем "пока
не это — истина". Для этого случая у Perl тоже есть ответ. Требуемый эффект дает
замена while на until:
until (выражение)
{
оператор_1;
...
оператор_n;
}
Обратите внимание на то, что в обеих формах операторы тела цикла полностью пропускаются,
если при анализе управляющего выражения выясняется, что цикл должен быть завершен.
Например, если в приведенном выше фрагменте программы пользователь введет возраст меньше
нуля. Perl пропустит тело цикла.
Вполне может случиться так, что управляющее выражение не даст циклу завершиться.
Это абсолютно допустимо, а иногда и желательно, поэтому ошибкой не считается. Например,
вы хотите, чтобы цикл повторялся все время, пока у вас нет ошибок, а после цикла ставите
какой-то код обработки ошибок. Эту схему можно использовать для программы-демона, которая
должна работать до тех пор, пока система не откажет.
Оператор do {} while/until
Оператор while/until, который вы видели в предыдущем разделе, проверяет условие в
начале каждого цикла, до входа в него. Если результат проверки условия — "ложь",
цикл не будет выполнен вообще.
Иногда возникает необходимость проверять условие не в начале, а в конце цикла. Для
этого в Perl есть оператор do {} while, который очень похож на обычный оператор
while (*3), за исключением того, что он проверяет выражение только после однократного
выполнения цикла.
do
{
оператор_1;
...
оператор_n;
}
while (выражение);
Perl выполняет операторы блока do. Дойдя до конца, он вычисляет выражение на
предмет истинности. Если выражение ложно, цикл завершается. Если выражение истинно,
весь блок выполняется еще раз, а затем выражение проверяется вновь.
Как и в обычном цикле while, условие проверки можно инвертировать, заменив
do {} while на do {} until. Выражение все равно проверяется в конце цикла,
но на обратное условие. В некоторых случаях, особенно сложных, такой способ
записи условия представляется более естественным.
$stops = 0;
do
{
$stops++;
print "Next stop?";
chomp($location = <STDIN>);
}
until ($stops > 5) || ($location eq 'home');
Оператор for
Еще одна конструкция Perl, предназначениая для организации цикла, — оператор for,
который выглядит подозрительно похоже на оператор for языков С и Java и работает
примерно так же. Вот он:
for (начальное_выражение; проверочное_выражение; возобновляющее_выражение)
{
оператор_1;
...
оператор_n;
}
Если преобразовать этот оператор в те формы, которые мы рассмотрели раньше, то
он станет таким:
начальное выражение;
while (проверочное выражение)
{
оператор_1;
...
оператор_n;
возобяовляющее_выражение;
}
В любом случае сначала вычисляется начальное выражение. Это выражение,
как правило, присваивает начальное значение переменной цикла, но никаких
ограничений относительно того, что оно может содержать, не существует;
оно даже может быть пустым (и ничего не делать). Затем вычисляется проверочное
выражение. Если полученное значение истинно, выполняется тело цикла, а затем
вычисляется возобновляющее выражение (как правило, используемое для инкрементирования
переменной цикла — но не только для этого). Затем Perl повторно вычисляет проверочное
выражение, повторяя необходимые действия.
Следующий фрагмент программы предназначен для вывода на экран чисел от
1 до 10, после каждого из которых следует пробел:
for ($i=1; $i<-10; $i++)
{
print "$i ";
)
Сначала переменная $i устанавливается в 1. Затем эта переменная сравнивается с
числом 10. Поскольку она меньше или равна 10, то выполняется тело цикла (один
оператор print), а затем вычисляется возобновляющее выражение ($i++), в результате
чего значение $i изменяется на 2. Поскольку эта переменная все еще меньше или равна
10, процесс повторяется до тех пор, пока в последней итерации значение 10 в $i не
изменится на 11. Поскольку переменная $i уже не меньше и не равна 10, цикл завершается
(при этом $i становится равным 11).
Оператор foreach
Еще одна циклическая конструкция — оператор foreach. Этот оператор получает
список значений и присваивает их по очереди скалярной переменной, выполняя с
каждым последующим присваиванием блок кода. Выглядит это так:
foreach $i (@список)
{
оператор_1;
...
оператор_n;
}
В отличие от C-shell, в Perl исходное значение этой скалярной переменной при
выходе из цикла автоматически восстанавливается; другими словами, эта скалярная
переменная локальна для данного цикла.
Вот пример использования оператора foreach:
@а = (1,2,3,4,5);
foreach $b (reverse @a)
{
print $b;
}
Эта программа выводит на экран 54321. Отметим, что список, используемый в
операторе foreach, может быть произвольным списочным литералом, а не просто
переменной-массивом. (Это справедливо для всех конструкций Perl, которым
требуется список.)
Имя скалярной переменной можно опустить. В этом случае Perl будет действовать
так, как будто вы указали имя переменной $_. Вы увидите, что переменная $_
используется по умолчанию во многих операциях языка Perl, поэтому ее можно рассматривать
как неявную временную переменную. (Все операции, в которых по умолчанию используется $_,
могут использовать и обычную скалярную переменную.) Например, функция print выводит значение
переменной $_, если другие значения не указаны, поэтому следующий пример дает такой
же результат, как и предыдущий:
@а = (1,2,3,4,5);
foreach (reverse @a)
{
print;
}
Видите, насколько использование неявной переменной $_ все упрощает? Когда вы
познакомитесь с другими функциями и операциями, которые по умолчанию используют
$_, то еще выше оцените полезность данной конструкции. Это один из тех случаев,
когда короткая конструкция более понятна, чем длинная.
Если список, над которым производятся циклические преобразования, состоит из реальных
переменных, а не получен при помощи функции, возвращающей списочное значение, то используемая
в цикле переменная представляет собой псевдоним для каждой переменной этого списка,
а не просто копию ее значения. Это значит, что, изменяя скалярную переменную, вы изменяете
и конкретный элемент в списке, который ей соответствует. Например:
@а = (3,5,7,9);
foreach $one (@a)
{
$one *= 3;
}
# @а теперь равно (9,15,21,27)
Обратите внимание на то, что изменение переменной $опе привело к изменению каждого
элемента массива @а.
Упражнения
- Напишите программу, которая запрашивает температуру окружающего воздуха и выводит
на экран слова "too hot", если температура выше 72 градусов (по Фаренгейту), а в
противном случае выводит "too cold".
- Модифицируйте программу из предыдущего упражнения так, чтобы она выводила на экран
"too hot", если температура выше 75 градусов, "too cold" при температуре ниже 68
градусов и "just right!" при температуре от 68 до 75 градусов.
- Напишите программу, которая читает список чисел (каждое из которых записано
в отдельной строке), пока не будет прочитано число 999, после чего программа выводит
сумму всех этих чисел. (Ни в коем случае не прибавляйте 999!) Например, если вы
вводите 1, 2, 3 и 999, программа должна ответить цифрой 6 (1 + 2 + 3).
- Напишите программу, которая читает список строковых значений (каждое из
которых занимает отдельную строку) и выводит его на экран в обратном порядке,
причем без проведения над списком операции reverse. (Вспомните, что <STDIN>
при использовании в списочном контексте читает список строковых значений, каждое
из которых занимает отдельную строку.)
- Напишите программу, которая выводит на экран таблицу чисел от 0 до 32 и
их квадратов. Попробуйте предложить способ, при котором список не обязательно
должен содержать все указанные числа, а затем способ, при котором их нужно
задавать все. (Чтобы выводимый результат выглядел более привлекательно, используйте
функцию printf "%5g %8g\n", $а, $b; которая выводит $а как пятизначное число,
а $b — как восьмизначное.
Ответы к упражнениям даны в приложении А.
Примечания
(*1) Внутренне все трактуется несколько иначе, но внешне все выполняется так, будто именно это и происходит.
(*2) Вот почему HTML — не язык программирования.
(*3) Ну, не совсем чтобы очень похож; управляющие директивы цикла, описанные в главе 9, в такой форме не работают.
| Назад
| Вперед |

