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

Ну вот дошли руки написать продолжение к посту Основы программирования на 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 делятся на две группы — производящие сравнение в числовом контексте и в строковом контексте. О том, что такое числовой и строковой контекст, было рассказано в моей предыдущей статье о программировании на Perl.

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

Сравнение строк в 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, ссылки и функции

  • http://twitter.com/twimov twimov

    Не хорошо пишешь, приучать людей к my, our, local надо бы сперва, а то они не видят и не понимают чем подобные выше коды грозят им :)

  • http://twitter.com/twimov twimov

    Не хорошо пишешь, приучать людей к my, our, local надо бы сперва, а то они не видят и не понимают чем подобные выше коды грозят им :)

  • http://eax.me/ Безумный Программист

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

  • Алексей

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

  • http://eax.me/ Безумный Программист

    Пожалуйста! Очень рад, что Вам понравилось. Именно так посты и задумывались — дать возможность сразу писать собственные perl-скрипты. Я считаю, что главное в программировании — это постоянная практика. Книжки и лекции в институте не дают и 10% эффекта.

  • http://twitter.com/ramznet Ramazan

    Спасибо)

  • blis_s

    Ну в любом случае нужно показать отличие между локальными, глобальными и динамическими переменными. А приучать к use strict нужно сразу, потом код чище будет.

  • ffsdmad

    слава богу этот язык сдох

    • perl_user

      скорее отомрет action script чем Perl и ваще
      use Perl or die; ))))

  • http://eax.me/ Безумный Программист

    Главное в это верить ;)))

  • Nameless One

    Спасибо, для новичка в самый раз!

  • 123

    Правилом хорошего тона при написании таких статей является проверка всех приводимых примеров. Если какой-то из них не работает новичку сложно разобраться в чём ошибка.

    Из-за ошибки в один символ этот пример работать не будет:

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

  • http://eax.me/ Безумный Программист

    Спасибо, ошибку исправил.

  • Vasilich

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

    1. А зачем в этом примере кавычки вокруг $_, что там интерполировать ??
    2. Зачем вообще писать $_?

    Вот более красивый и быстрый вариант того же самого:

    for(1..4) {
    print;
    }

    или:

    print for (1..4);