|
|
|
урок: предыдущий следующий
Блок операторов — это последовательность операторов, заключенная в парные фигурные скобки. Блок операторов выглядит следующим образом:
{
первыи_оператор;
второй_оператор;
третий_оператор;
последний_оператор;
}
|
Perl выполняет операторы по очереди, начиная с первого и кончая последним. (Позднее вы узнаете о том, как можно изменять порядок выполнения в блоке, но пока достаточно и этого.)
Синтаксически блок операторов принимается вместо любого одиночного оператора, но обратное не верно.
Завершающая точка с запятой, стоящая за последним оператором, не обязательна. Таким образом, вы можете разговаривать на языке Perl с С-акцентом (точка с запятой присутствует) или с паскалевским акцентом (точка с запятой отсутствует). Чтобы облегчить последующее добавление операторов, мы обычно рекомендуем опускать точку с запятой лишь в том случае, если блок занимает целую строку. Сравните примеры этих стилей в следующих двух блоках if:
if ($ready) { $hungry++ }
if ($tired) {
$sleepy = ($hungry + 1) * 2;
}
|
Следующей по сложности управляющей структурой является оператор if. Эта конструкция состоит из управляющего выражения (проверяемого на истинность) и блока. В ней также может быть часть, начинающаяся оператором else, за которой следует еще один блок операторов. Другими словами, все это выглядит так:
if (выражение) {
оператор_1
оператор_2
оператор_3
}
else {
оператор_1
оператор_2
оператор_3
}
|
(Если вы любите программировать на С и Java, то для вас должна быть вполне очевидной обязательность фигурных скобок. Они устраняют необходимость в применении правила "путающего зависшего else".)
Во время выполнения Perl-программы вычисляется управляющее выражение. Если оно истинно, то выполняется первый блок операторов в приведенном выше примере. Если выражение ложно, то выполняется второй блок.
Что же такое "истина" и "ложь"? В Perl эти правила несколько странноваты, но, тем не менее, дают ожидаемые результаты. Управляющее выражение вычисляется как строковая величина в скалярном контексте (если это уже строка, ничего не изменяется, а если это число, то оно преобразуется в строку*). Если данная строка либо пуста (т.е. имеет нулевую длину), либо состоит из одного символа "О" (цифры нуль), то значение выражения — "ложь". Все остальное автоматически дает значение "истина". Почему же в Perl столь забавные правила? А потому, что это облегчает условный переход не только по нулю (в противоположность ненулевому числу), но и по пустой (в противоположность непустой) строке, причем без необходимости создания двух версий интерпретируемых значений "истина" и "ложь". Вот несколько примеров интерпретации этих значений.
* Внутренне все трактуется несколько иначе, но внешне все выполняется так, будто именно это и происходит.
0 # преобразуется в "0", поэтому "ложь"
1-1 # дает в результате 0, затем преобразуется в "0", поэтому "ложь"
1 # преобразуется в "1", поэтому "истина"
"" # пустая строка, поэтому "ложь"
"1" # не "" или "0", поэтому "истина"
"00" # не "" или "0", поэтому "истина" (это странно, поэтому будьте настороже)
"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++;
}
|
Заменить if на unless — это все равно что сказать "Если управляющее выражение ложно, сделать..." (Оператор unless может содержать блок else, как и оператор if.)
Если у вас больше двух возможных вариантов, введите в оператор if ветвь elsif, например:
if (выражение один) {
оператор_1_при_истине_один;
оператор_2_при_истине_один;
оператор_3_при_истине_один;
}
elsif (выражение_два) {
оператор_1_при_истине_два;
оператор_2_при_истине_два;
олератор_3_при_истине_два;
}
elsif (варажение_три) {
оператор_1_при_истине_три;
оператор_2_при_истине_три;
оператор_3_при_истине_три;
}
else {
оператор_1_при_всеи_ложных;
оператор_2_при_всех_ложных;
оператор_3_при_всех_ложных;
}
|
Все управляющие выражения вычисляются по очереди. Если какое-либо выражение истинно, то выполняется соответствующая ветвь, а все остальные управляющие выражения и соответствующие блоки операторов пропускаются. Если все выражения ложны, то выполняется ветвь else (если таковая имеется). Присутствие блока else не обязательно, но он никогда не помешает. В программе может быть столько ветвей elsif, сколько вам необходимо.
Ни один язык программирования не был бы полным без какой-нибудь формы организации цикла* (повторяющегося выполнения блока операторов). Perl может организовать цикл с помощью оператора while:
while (выражение) {
оператор_1;
оператор_2;
оператор_3;
}
|
Чтобы выполнить оператор 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;
оператор_2;
оператор_3;
}
|
* Вот почему HTML — не язык программирования.
Обратите внимание на то, что в обеих формах операторы тела цикла полностью пропускаются, если при анализе управляющего выражения выясняется, что цикл должен быть завершен. Например, если в приведенном выше фрагменте программы пользователь введет возраст меньше нуля. Perl пропустит тело цикла.
Вполне может случиться так, что управляющее выражение не даст циклу завершиться. Это абсолютно допустимо, а иногда и желательно, поэтому ошибкой не считается. Например, вы хотите, чтобы цикл повторялся все время, пока у вас нет ошибок, а после цикла ставите какой-то код обработки ошибок. Эту схему можно использовать для программы-демона, которая должна работать до тех пор, пока система не откажет.
Оператор do {} while / until.
Оператор while/until, который вы видели в предыдущем разделе, проверяет условие в начале каждого цикла, до входа в него. Если результат проверки условия — "ложь", цикл не будет выполнен вообще.
Иногда возникает необходимость проверять условие не в начале, а в конце цикла. Для этого в Perl есть оператор do {} while, который очень похож на обычный оператор while*, за исключением того, что он проверяет выражение только после однократного выполнения цикла.
* Ну, не совсем чтобы очень похож; управляющие директивы цикла, описанные в главе 9, в такой форме не работают.
do (
оператор_1;
опвратор_2;
оператор_3;
}
while выражение;
|
Perl выполняет операторы блока do. Дойдя до конца, он вычисляет выражение на предмет истинности. Если выражение ложно, цикл завершается. Если выражение истинно, весь блок выполняется еще раз, а затем выражение проверяется вновь.
Как и в обычном цикле while, условие проверки можно инвертировать, заменив do {} while на do {} until. Выражение все равно проверяется в конце цикла, но на обратное условие. В некоторых случаях, особенно сложных, такой способ записи условия представляется более естественным.
$stops = 0;
do {
$stops++;
print "Next stop? " ;
chomp($location = <STDIN>) ;
}
until $stops > 5 || $location eq 'home';
|
Еще одна конструкция Perl, предназначенная для организации цикла, — оператор for, который выглядит подозрительно похоже на оператор for языков С и Java и работает примерно так же. Вот он:
for ( начальное_выражение; проверочное_выражение; возобновляющее выражение) {
оператор_1;
оператор_2;
оператор_3;
}
|
Если преобразовать этот оператор в те формы, которые мы рассмотрели раньше, то он станет таким:
начальное выражение;
while (проверочное выражение) {
оператор_1;
оператор_2;
оператор_3;
возобяовляющее_выражение;
}
|
В любом случае сначала вычисляется начальное выражение. Это выражение, как правило, присваивает начальное значение переменной цикла, но никаких ограничений относительно того, что оно может содержать, не существует; оно даже может быть пустым (и ничего не делать). Затем вычисляется проверочное выражение. Если полученное значение истинно, выполняется тело цикла, а затем вычисляется возобновляющее выражение (как правило, используемое для инкрементирования переменной цикла — но не только для этого). Затем 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 $i (@список) {
оператор_1;
оператор_2;
оператор_3;
}
|
В отличие от C-shell, в Perl исходное значение этой скалярной переменной при выходе из цикла автоматически восстанавливается; другими словами, эта скалярная переменная локальна для данного цикла.
Вот пример использования оператора foreach:
@a = (1,2,3,4,5);
foreach $b (reverse @a) {
print $b;
}
|
Эта программа выводит на экран 54321. Отметим, что список, используемый в операторе foreach, может быть произвольным списочным литералом, а не просто переменной-массивом. (Это справедливо для всех конструкций Perl, которым требуется список.)
Имя скалярной переменной можно опустить. В этом случае Perl будет действовать так, как будто вы указали имя переменной $_. Вы увидите, что переменная $_ используется по умолчанию во многих операциях языка Perl, поэтому ее можно рассматривать как неявную временную переменную. (Все операции, в которых по умолчанию используется $_, могут использовать и обычную скалярную переменную.) Например, функция print выводит значение переменной $_, если другие значения не указаны, поэтому следующий пример дает такой же результат, как и предыдущий:
@a = (1,2,3,4,5);
foreach (reverse @a) {
print ;
}
|
Видите, насколько использование неявной переменной $_ все упрощает? Когда вы познакомитесь с другими функциями и операциями, которые по умолчанию используют $_, то еще выше оцените полезность данной конструкции. Это один из тех случаев, когда короткая конструкция более понятна, чем длинная.
Если список, над которым производятся циклические преобразования, состоит из реальных переменных, а не получен при помощи функции, возвращающей списочное значение, то используемая в цикле переменная представляет собой псевдоним для каждой переменной этого списка, а не просто копию ее значения. Это значит, что, изменяя скалярную переменную, вы изменяете и конкретный элемент в списке, который ей соответствует. Например:
@a = (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, з и 999, программа должна ответить цифрой 6 (1 + 2 + 3).
- Напишите программу, которая читает список строковых значений (каждое из которых занимает отдельную строку) и выводит его на экран в обратном порядке, причем без проведения над списком операции reverse. (Вспомните, что <stdin> при использовании в списочном контексте читает список строковых значений, каждое из которых занимает отдельную строку.)
- Напишите программу, которая выводит на экран таблицу чисел от 0 до 32 и их квадратов. Попробуйте предложить способ, при котором список не обязательно должен содержать все указанные числа, а затем способ, при котором их нужно задавать все. (Чтобы выводимый результат выглядел более привлекательно, используйте функцию
|
printf "%5g %8g\n", $a, $b;
|
которая выводит $а как пятизначное число, а $b — как восьмизначное.
|
|
|