Основы Perl — условные операторы и циклы

25 февраля 2010

Ну вот дошли руки написать продолжение к посту Основы программирования на Perl. В этой части речь пойдет об условных операторах и циклах.

Условные операторы

Как всегда, начнем сразу с примеров.

$a = shift;
if($a > 10) {
  print "a > 10\n";
}

Программистам на C-подобных языках эта конструкция должна быть до боли знакома, так что комментировать тут особо нечего. Скажу лишь, что в отличие от Си, опустить фигурные скобки тут нельзя. Точнее говоря, способ есть, но о нем чуть ниже. Конструкции if-else и if-else-if-… на языке Perl выглядят следующим образом:

$a = shift;
if($a > 10) {
  print "a > 10\n";
} else {
  print "a <= 10\n";
}

if($a > 0) {
  # do something
} elsif($a == 0) {
  # do something
} else {
  # do something else
}

В общем, все все как мы и ожидаем за одним исключением. Никакого «else if» в Perl нет — вместо этого следует использовать elsif и только его. Elsif может повторяться несколько раз, else использовать не обязательно, фигурные скобки опустить нельзя.

В отличие от других языков программирования, в Perl также предусмотрен оператор unless. Следующие два куска кода делают одно и то же:

unless($a == 0) {
  # '... если только a не равно нулю'
  ...
}
if($a != 0) {
  # то же самое
  # ...
}

Unless можно использовать в сочетании с elsif и else, однако никакого «elsunless» в Perl нет.

В Perl есть возможность уменьшить объем кода, если в случае выполнения условия должна быть выполнена только одна строка кода. Следующие примеры делают одно и то же:

# если условие истинное - завершить скрипт с кодом 1
if($a > $b) {
  exit 1;
}

# если b == c, продолжить выполнение скрипта
unless($b == $c) {
  exit 2;
}
# если условие истинное - завершить скрипт с кодом 1
exit 1 if($a > $b);
# если b == c, продолжить выполнение скрипта
exit 2 unless($b == $c);

При этом скобки в последнем примере можно не использовать:

# если условие истинное - завершить скрипт с кодом 1
exit 1 if $a > $b;

Если вы пишите на Java/PHP или другом Си-подобном языке, такая конструкция скорее всего будет для вас непривычной, но на практике она действительно удобна. В русском языке мы ведь тоже обычно говорим «завершить программу, если …», а не «если …, то …».

Также, как в C/C++ и PHP, в Perl имеется тернарный оператор ?:. Работает он аналогично конструкции if-else, только внутри выражений:

if($a > $b) {
  $a = $a / $b;
} else {
  $a = $b / $a;
}

# аналогичный код, использующий оператор "знак вопроса"
# одна строчка кода вместо пяти
$a = $a > $b ? $a / $b : $b / $a;

Также хотелось бы сказать пару слов об операторах сравнения. Операторы сравнения в языке Perl делятся на две группы — производящие сравнение чисел и сравнение строк.

Оператор Сравниваем числа Сравниваем строки
больше > gt
меньше < lt
больше или равно >= ge
меньше или равно <= le
равно == eq
неравно != ne

Как вы помните, скаляры в Perl можно интерпретировать либо как строки, либо как числа. Например, число 123 больше числа 45, однако строка «123» меньше строки «45». Вот для чего потребовалось несколько групп операторов сравнения. Сравнение строк в Perl производится таким же образом, как и в других современных языках программирования, потому я надеюсь, что тут вопросов не возникает.

Циклы for, foreach, while/until, do..while/until

Цикл for прекрасно знаком всем программистам:

# вывести строку '0 1 2 3 4'
for($i = 0; $i < 5; $i++) {
  print "$i ";
}

В круглых скобках через точку с запятой записывается:

  1. Код, выполняемый перед началом цикла.
  2. Условие, проверяемое перед началом (а не в конце, как думают многие) каждой итерации. Если оно ложно, выполнение цикла завершается.
  3. Код, выполняемый после каждой итерации.

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

Цикл foreach должен быть хорошо знаком программистам на PHP:

@arr = (0, 1, 2, 3, 4);
# вывести строку '0 1 2 3 4'
foreach $i (@arr) {
  print "$i ";
}

Тело цикла foreach выполняется для каждого элемента массива, указанного в скобках. Важная особенность foreach — в переменную $i не копируется элемент массива @arr, как думают многие. Переменная $i в теле цикла — это и есть сам элемент массива. Таким образом, следующий код увеличивает на единицу значение каждого элемента массива @arr:

$i = 19880508;
foreach $i (@arr) {
  $i++;
}
# $i по прежнему равен 19880508

Также foreach можно использовать для работы с хэшами:

%hash = (
  aaa => 1,
  bbb => 2,
);
# функция keys возвращает массив, содержащий все ключи хэша
foreach $k (keys %hash) {
  print "$k => $hash{$k}\n";
}

В Perl цикл foreach имеет короткую форму:

# если Вы забыли, что делает оператор qw,
# вернитесь к первой части "основ программирования на Perl"
for $i (qw/1 2 3/) {
  print "$i ";
}

То есть фактически везде вместо foreach можно писать просто for. Perl не перепутает такую запись с настоящим циклом for, потому что в последнем нужно писать точки с запятой и так далее.

Циклы while, until и do работают точно так же, как и в C++ или Pascal/Delphi:

# выводим '1 2 3 4 5' четырьмя разными способами

$i = 0;
while($i < 5) { # пока $i меньше пяти
  print ++$i." ";
}
print "\n"; # новая строка

$i = 0;
until($i == 5) { # пока $i не станет равно пяти
  print ++$i." ";
}
print "\n";

$i = 0;
do {
  print ++$i." ";
} while ($i < 5); # проверка в конце цикла
print "\n";

$i = 0;
do {
  print ++$i." ";
} until ($i == 5);
print "\n";

По аналогии с операторами if и unless, существует сокращенная форма записи циклов:

$i = 0;
print ++$i." " while($i < 5);
print "\n";

$i = 0;
print ++$i." " until($i == 5);
print "\n";

print "$_ " for(qw/1 2 3 4 5/); # можно и foreach(qw/1 2 3 4 5/);
print "\n";

Обратите внимание на последний цикл foreach. Мы ведь помним, что это сокращенная запись foreach, а не цикл for, верно? Последний кстати не имеет короткой записи. Так вот, здесь не было указано имя переменной, с помощью которой мы будем обращаться к элементам массива. В сокращенной записи foreach ее нельзя использовать. В этом случае используется специальная переменная — $_. Следующий пример также вполне рабочий:

for(qw/1 2 3 4/) {
  print "$_";
}

Его, кстати, можно переписать следующим образом:

for(1..4) {
  print "$_";
}

Оператор .. (две точки) принимает два операнда и возвращает массив, элементы которого представляют собой все числа от аргумент 1 до аргумент 2 включительно, упорядоченные по возрастанию. Другими словами следующие две строчки делают одно и то же:

@arr1 = 1..4;
@arr2 = qw/1 2 3 4/;

Также оператор «две точки» можно использовать со строковыми операндами:

# массив строк - aaa, aab, aac, ..., zzzz
@arr1 = "aaa".."zzzz";

Операторы last и next

Операторы last и next в Perl аналогичны операторам break и continue в языке программирования C, только они короче на 1 и 4 символа соответственно. Примеры их использования:

for($i = 0; $i < 10; $i++) {
  next if $i == 2; # перейти к следующей итерации
  print "$i ";
  if($i == 8) { last; } # выйти из цикла
}
print "\n";
# будет выведено: 0 1 3 4 5 6 7 8

Со вложенными циклами немного сложнее — тут нужно использовать метки:

ATTEMPT:
for($i = 1; $i <= 3; $i++) {
  for($j = 1; $j <= 3; $j++) {
    if($i == $j and $j  == 3) {
      last ATTEMPT; # выходим из цикла for($i = ...
    }
    print "i = $i, j = $j\n";
  }
}

Кстати, метки также используются оператором goto:

goto ATTEMPT;

Как вы, наверное, уже многократно слышали, этот оператор устарел и используя его в своем коде, вы рискуете быть засмеянными своими коллегами. А в остальном — это вполне нормальный оператор.

Думаю, на сегодня это все. Если у вас есть вопросы по языку Perl (любые — не только по теме поста), смело задавайте их в комментариях. Чтобы не пропустить новые посты, посвященные основам программирования на Perl, подпишитесь на обновления блога.

Дополнение: Очень познавательная статья про операторы диапазона опубликована на HabraHabr.

Далее: Директива use strict, ссылки и функции

Метки: .


Вы можете прислать свой комментарий мне на почту, или воспользоваться комментариями в Telegram-группе.