Основы Perl — use strict, ссылки и функции

14 марта 2010

Это третья часть цикла статей, посвященных основам программирования на Perl. Если вы пропустили первые две части, ознакомьтесь с оглавлением.

Директива use strict

Вспомните, как мы объявляли переменные в предыдущих статьях?

#/usr/bin/perl

$alpha = 1;
$beta = 2;
# ...
$alpha = $alpha + $beta;
# alpha равно 3

Однако что произойдет, если мы случайно сделаем опечатку в имени одной из переменных?

# опечатка в имени переменной - bAta вместо bEta
$alpha = $alpha + $bata;
# alpha равно 1

Программист допускает подобные ошибки довольно часто — чаще, чем думают многие. При этом, из-за опечатки в имени одной переменной, поведение программы может меняться совершенно неожиданным образом. Лишние итерации циклов, вывод сообщений, которых, казалось бы, скрипт никак не должен был производить — вот лишь малая часть списка возможных «побочных эффектов». Обычно исправление таких ошибок — дело непростое, ведь программист начинает искать их с конца, с того места, где программа повела себя неверным образом. То есть в том месте, где ошибки на самом деле нет.

Для решения этой проблемы возьмите за правило в любых скриптах, которые сложнее «hello world», использовать директиву use strict. Если в скрипте используется эта директива, объявление переменных следует начинать со слова my:

#/usr/bin/perl
use strict;

my $alpha = 1;
my $beta = 2;
# ...
$alpha = $alpha + $beta;
# alpha равно 3

Совсем не сложно, правда? Что же произойдет, если мы сделаем опечатку в имени переменной? Поскольку переменная $bata ранее не была объявлена, интерпретатор будет ругаться на синтаксическую ошибку и скрипт просто не выполнится, пока мы не исправим опечатку.

Global symbol "$bata" requires explicit package name at ./t.pl line 7.
Execution of ./t.pl aborted due to compilation errors.

Еще раз повторяю — при написании скрипта, размером более 10-и строк кода, всегда используйте директиву use strict. В противном случае вы рискуете потратить много времени на исправление совершенно «мистических» ошибок.

Ссылки

Помните, в первой части «основ Perl» мы использовали массивы и хэши. Любопытный читатель спросит — а как же быть с многомерными массивами, массивами хэшей и другими сложными структурами? Неужели Perl их не поддерживает? На самом деле это не так, и скоро вы в этом убедитесь.

Для начала рассмотрим простую задачу — объявить двумерный массив. Вот один из способов это сделать:

my @arr1 = (1, 2, 3);
my @arr2 = (4, 5, 6);
my @arr3 = (7, 8, 9);

my @matrix = (\@arr1, \@arr2, \@arr3);

for(my $i = 0; $i < 3; $i++) {
  for(my $j = 0; $j < 3; $j++) {
    print $matrix[$i][$j]." ";
  }
  print "\n";
}

Здесь был объявлен массив из трех элементов @matrix, элементами которого являются ссылки на массивы @arr1, @arr2 и @arr3. Смотрите, что происходит — в массиве по прежнему хранятся скаляры, но поскольку они, эти скаляры, представляют собой ссылки на массивы, мы фактически имеем дело с многомерным массивом!

Как вы уже могли догадаться, получение ссылки на переменную происходит с помощью оператора «обратный слэш»:

$sref = \ $scalar; # ссылка на скаляр
$aref = \ @array; # ссылка на массив
$href = \ %hash; # ссылка на хэш

Другой способ получить ссылку на массив — использовать квадратные скобки:

my @matrix = ([@arr1], [@arr2], [@arr3]);
my $aref = [qw/aaa bbb ccc/];
my $aref2 = [4, 5, 6];

Аналогичный способ получить ссылку на хэш — использовать фигурные скобки:

my $aref2 = [4, 5, 6]; # ссылка на массив
my $href = {aaa => 7, bbb => 8, ccc => 9}; # ссылка на хэш

Дополнение: Как совершенно верно заметил в комментариях cub@uanic, с помощью квадратных и фигурных скобок вы получите ссылку на новый массив или хэш. Потому используйте эти операторы только при объявлении переменных, или если вам действительно нужна копия массива или хэша.

Тут главное не забывать, что ссылка представляет собой скалярную переменную. Разыменование ссылок происходит с помощью одного из следующих способов:

# используем оператор "стрелка"
print $aref2->[0]."\n"; # выведет 4
print $href->{aaa}."\n"; # выведет 7

my @arr_copy = @{$aref2}; # копируем массив
my %hash_copy = %{$href}; # копируем хэш

Учитывая все вышесказанное, давайте попробуем поработать с двумерным массивом и массивом, элементами которого являются хэши.

my @matrix = ( # объявляем массив
  [1, 2, 3], # элементы которого - ссылки на массив
  [4, 5, 6],
  [7, 8, 9]);

print $matrix[0]->[1]."\n"; # выведет 2
print $matrix[2][2]."\n"; # выведет 9, "стрелку" можно опустить

my @points = ( # массив точек
  { x => 7, y => 13 },
  { x => -3, y => 2 },
  { x => 0, y => -9});

# координаты первой точки
print $points[0]->{x}.";".$points[0]->{y}."\n";

# координаты второй точки
# аналогично предыдущему примеру - оператор "стрелка" можно опустить
print $points[1]{x}.";".$points[1]{y}."\n";

Оператор «стрелка» всегда можно опускать между индексами. То есть следующие две строчки кода абсолютно аналогичны:

$ref->[$i]->[$j]->[$k]++;
$ref->[$i][$j][$k]++; # то же самое, только короче

Ссылки и утечка памяти

При работе с ссылками важно помнить о том, что интерпретатор Perl не освободит память, выделенную под массив, хэш или скаляр, пока на него существует хотя бы одна ссылка.

my @arr = ([1, 2, 3], [4, 5 ,6]);
my $arr_ref = $arr[1]; # ссылка на массив (4,5,6)

@arr = undef; # аналогично вызову undef() в PHP

print $arr_ref->[1]."\n"; # выведет 5!
# ^ тут, кстати, "стрелку" опустить нельзя
# иначе Perl решит, что мы работаем с
# не объявленной переменной @arr_ref

$arr_ref = undef;
# вот только теперь память, выделенная под
# безымянный массив (4, 5, 6) будет освобождена

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

Функции

Я считаю, что вы уже знаете, что такое функции и процедуры, для чего они нужны. Функции в Perl похожи на функции в языке программирования C в том плане, что они могут принимать произвольное число аргументов. Аналогично языку C, нет разницы между функциями и процедурами, как в Pascal. Пример объявления и вызова функции:

#!/usr/bin/perl
use strict;

sub myfunc {
  my($alpha, $beta) = @_;
  $alpha + $beta;
}

print myfunc(2, 3)."\n";

Результат последней операции, выполненной внутри функции, будет возвращен в качестве результата. С помощью оператора return можно вернуть результат в произвольном месте функции, не только в конце:

#!/usr/bin/perl
use strict;

sub myfunc {
  my($alpha, $beta) = @_;
  if($alpha > $beta) {
    # прекратить выполнение функции и вернуть результат
    return $alpha - $beta;
  }
  $beta - $alpha;
}

print myfunc(2, 3)."\n";

Как видите, для передачи аргументов функции, используется специальная переменная @_. Если в качестве аргумента необходимо передать массив или хэш, используйте ссылки. Помните, что в этом случае происходит передача аргумента по ссылке, а не по значению:

#!/usr/bin/perl
use strict;

sub setval {
  my($ref, $val) = @_;
  $ref->{val} = $val;
}

my %hash;
setval(\%hash, 14);
print "$hash{val}\n"; # выведет 14

Думаю, на сегодня это все. В следующих частях я расскажу о работе с файлами, глобах, функциях eval и system, операторах sort и grep (как выяснилось, следующий урок и так оказался очень объемным, потому зачеркнутое в него не вошло). Регулярные выражения, скорее всего, не войдут в «основы Perl» — при желании, этому вопросу можно посветить отдельную статью, если не серию статей.

Если есть вопросы — задавайте. Чтобы не пропустить новые материалы, следите за обновлениями блога по RSS или в Twitter. Удачи в изучении Perl!

Далее: Ввод/вывод, файлы, каталоги и глобы

Метки: .


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