Справочник по Perl: Функции, относящиеся к области видимости переменных. Изучаем Perl

Строковые функции
Сейчас мы рассмотрим строковые функции. С помощью этих функций вы можете определить длину строки, произвести поиск подстроки или поменять местами символы в строке. Ниже приведены строковые функции, имеющиеся в Perl:

chomp (string), chomp (array) использует значение специальной переменной $/ для последнего символа строки string или каждого элемента массива array. Последний символ будет удален только в том случае, если он равен значению переменой $/.

chop (string), chop (array) делает то же самое, что и предыдущая функция, но в качестве результата эта функция возвращает сам удаленный символ.

chr (number) возвращает символ из ASCII-таблицы с кодом number. Например, chr(65) возвратит символ "A".

crypt (string1, string2) шифрует строку string 1. Perl не предоставляет механизмов для дешифрования строки.

index (string, substring, position) возвращает позицию первого вхождения строки substring в строке string считая от позиции position. Если параметр position не задан, то сравнение строк ведется от начала строки string.

join (string, array) возвращает строку, в которой все элементы массива array соединены строкой string. Например, join (">>", ("a","b","c")) возвратит строку "a>>b>>c";

lc (string) возвратит строку, где все символы прописные. Например, lc ("ABCD") возвратит "abcd"

lcfirst (string) возвратит строку, в которой только первый символ прописной. Например, lcfirst ("ABCD") возвратит "aBCD"

length (string) возвращает длину строки.

rindex (string, substring, position) то же, что и index (смотрите выше), но возвращает позицию последнего вхождения.

substr (string, offset, length) возвращает часть строки string, как определено параметрами offset (смещение) и length (длина). Если length не указан, возвращается все, что находится на промежутке от offset до конца string. Отрицательные значения offset могут использоваться для сканирования с правого края строки string.

uc (string) то же самое, что и lc (смотрите выше), но все символы заглавные.

ucfirst (string) то же самое, что lcfirst (смотрите выше), но символ заглавный.

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

Некоторые функции из приведенных выше используют для своей работы специальную переменную $_, о которой вы узнаете в главе 9 "Файлы" и главе 12 "Использование специальных переменных".

Следующие несколько примеров демонстрируют применение некоторых строковых функций. После того, как вы поймете работу нескольких строковых функций, вы сможете правильно применять и остальные.

Пример: изменение значения строки

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

$firstVar = substr("0123BBB789", 4, 3);


Программа напечатает:

firstVar = BBB

Функция возвратит строку, начиная с 5-го символа, и длиной в 3 символа.

Применение данной функции становится гораздо более интересным, когда вы используете ее слева от оператора присваивания:

$firstVar = "0123BBB789";
substr($firstVar, 4, 3) = "AAA";
print("firstVar = $firstVar\n");

Программа напечатает:

FirstVar = 0123AAA789

Пример: поиск строки

Другая интересная задача, которую вы можете решить с помощью строковх функций - это поиск заданной подстроки в строке. Например, у вас есть полное имя файла, включая путь: "C:\\WINDOWS \\TEMP\\WSREWE.DAT", а вам нужно получить из него только имя файла. Вы можете это сделать, найдя последний обратный слеш (символ "\"), а за тем применив функцию substr().

Помните, что в строке для указания символа "\" вы должны использовать двойной символ "\\". Если вы подзабыли материал, обратитесь к главе 2 "Числовые и стринговые литералы".

$pathName = "C:\\WINDOWS\\TEMP\\WSREWE.DAT";
$position = rindex($pathName, "\\") + 1;
$fileName = substr($pathName, $position);
print("$fileName\n");

Программа напечатает:

WSREWE.DAT

Мы не указали здесь третий параметр функции substr() - length, - это потому что нам нужно взять подстроку до конца исходной строки.

Функции для работы с массивами.

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

Функции, которые вы можете использовать для работы с массивами:

defined (variable) возвращает true если variable имеет действительное значение, и false если variable пока не было присвоено значение. Это относится не только к массивам, данные любого типа могут быть проверены таким образом. В отношении подобных операций с ключами ассоциативных массивов смотрите функцию exists().

delete (key) удаляет пару "ключ-значение" из данного ассоциативного массива. Если вы удалите подобным образом значение из массива окружения %ENV, то изменится окружение только текущего процесса.

each (assoc_array) возвращает двухэлементый список, содержащий ключ и значение из заданного ассоциативного массива. Если уже был прочитан последний элемент массива, возвращается пустой список.

exists (key) возвращает true, если key является ключем заданного ассоциативного массива. Например, exists ($array("Orange")) возвратит true, если ассоциативный массив %array имеет ключ со значением "Orange".

join (string, array) возвращает строку, в которой все элементы массива array соединены строкой string. Например, join (">>", ("a","b","c")) возвратит строку "a>>b>>c".

keys (assoc_array) возвращает список всех ключей, имеющихся в заданном ассоциативном массиве. Список не составляется в каком-либо определенном порядке.

map (expression, array) вычисляет выражение expression для каждого элемента массива array. Специальная переменная $ присваивается каждому элементу массива array перед вычислением выражения expression.

pack (string, array) создает бинарную структуру, используя string как шаблон, массива array. Подробнее смотрите в главе 8 "Ссылки".

pop (array) возвращает последнее значение массива array, уменьшая его размер на единицу.

push (array1, array2) добавляет содержимое массива array1 к массиву array2, изменяя размер массива array1 должным образом.

reverse (array) когда используется в контексте массива, изменяет порядок следования элементов массива на противоположный. Когда же использется в скалярном контексте, массив преобразуется в строку, а уже строка подвергается реверсивному изменению.

scalar (array) рассматривает массив как скаляр и возвращает количество элементов в массиве.

shift (array) возвращает первое значение массива array, уменьшая его размер на единицу.

sort (array) возвращает список, содержащий элементы массива array, отсортированные в заданном порядке. Подробнее смотрите главу 8 "Ссылки".

splice (array1, offset, length, array2) заменяет элементы массива array1 элементами массива array2. Возвращает список, содержащий все элементы, которые были удалены.

split (pattern, string, limit) разбивает строку string на части, принимая за границу значение параметра pattern. Например, ($a, $b, $c) = split ("::", "1::2::3") присвоит переменным $a, $b, $c значения "1", "2", "3" соответственно. Если же результат используется в скалярном контексте, то функция возвращает количество найденных таким образом элементов.

undef (variable) всегда возвращает неопределенное значение. Кроме того, "разопределяет" переменную variable, которая должна быть скаляром, целым массивом или именем подпрограммы.

unpack (string, array) совершает действия, полностью противоположные деяниям функции pack() - смотрите выше.

unshift (array1, array2) добавляет элементы массива array1 к началу массива array2. Добавляемые элементы сохраняют оригинальный порядок. Возвращает новый размер массива array1.

values (assoc_array) возвращает все значения заданного ассоциативного массива. Возвращаемый список не формируется в каком-либо определенном порядке.

Как и в случае со строковыми функциями, мы рассмотрим в примерах только несколько функций для работы с массивами.

Пример: печать ассоциативного массива.

Как мы уже знаем, функция each() возвращает пару ключ-значение ассоциативного массива. Это называется итерацией элементов массива. То есть, функция начинает работу от начала массива, и заканчивает только в случае достижения конца массива.

%array = ("100", "Green", "200", "Orange");
while (($key, $value) = each(%array)) {
print("$key = $value\n");
}

Программа напечатает:

100 = Green
200 = Orange

По достижении конца массива, функция возвращает false.

Пример: проверка существования элемента массива

Перед тем как присваивать какому-либо элементу массива значение, вы можете проверить - существует ли вообще данный элемент - с помощью функции defined(). Это очень удобно если вы, например, читаете какие-либо значения с диска, и не хотите перезаписывать ими те значения, которые уже содержатся в памяти. Предположим, у вас имеется база данных с адресами клиентов, и вы хотите удостовериться, что в ней нет дубликатов. Вы считываете с диска адреса один за одним, и записываете их в качестве значений в ассоциативный массив, используя имена клиентов как ключи массива. Если имя клиента как ключ массива уже имеется в массиве, то его новый адрес помечается.

Поскольку мы ще не говорили о работе с файлами, то нам потребуется эмулировать работу с диском. И вместо адресов клиентов мы будем использовать номер клиента в паре с его именем. Сначала мы посмотрим, что получится когда в ассоциативном массиве два занчения имеют один ключ.





print("$key, $value\n");
};
sub createPair{
my($key, $value) = @_ ;
$array{$key} = $value;
};

Программа напечатает:

100, George Orwell
200, Grace Kelly

Как вы видите, первая пара массива была перезаписана третьей парой. Таким образом, чтобы избежать ошибок, нам нужно иметь возможность достоверно проверить - нет ли в массиве уже такого ключа, который мы собираемся использовать. Следующая программа показывает, как это делается:

createPair("100", "Kathy Jones");
createPair("200", "Grace Kelly");
createPair("100", "George Orwell");
while (($key, $value) = each %array) {
print("$key, $value\n");
};
sub createPair{
my($key, $value) = @_ ;
while (defined($array{$key})) {
$key++;
}
$array{$key} = $value;
};

Программа напечатает:

100, George Orwell
101, Kathy Jones
200, Grace Kelly

Как вы видите, программа заметила, что ключ "100" уже существует, и новые данные добавила в массив с ключем "101". Если бы, например, ключ "101" уже существовал в массиве, то новые данные таким образом добавились бы с ключем "102".

Директива 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 ./test.pl line 7.
Execution of ./test.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 for(my $j = 0; $j print $matrix[$i][$j]." ";
}
print "\n";
}


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

my @matrix = ([@arr1], [@arr2], [@arr3]);
my $aref = ;
my $aref2 = ;


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

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


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

# используем оператор "стрелка"
print $aref2->."\n"; # выведет 4
print $href->{aaa}."\n"; # выведет 7
my @arr_copy = @{$aref2}; # копируем массив
my %hash_copy = %{$href}; # копируем хэш


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

my @matrix = (# объявляем массив
, # элементы которого - ссылки на массив
,
);
print $matrix->."\n"; # выведет 2
print $matrix."\n"; # выведет 9, оператор "стрелка" можно опустить
my @points = (# массив точек
{ x => 7, y => 13 },
{ x => -3, y => 2 },
{ x => 0, y => -9});
# координаты первой точки
print $points->{x}.";".$points->{y}."\n";
# координаты второй точки
# аналогично предыдущему примеру - оператор "стрелка" можно опустить
print $points{x}.";".$points{y}."\n";


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

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


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

my @arr = (, );
my $arr_ref = $arr; # ссылка на массив (4,5,6)
@arr = undef; # аналогично вызову undef() в PHP
print $arr_ref->."\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. В этой главе мы обратим свой взор на функции. Функции - это блоки кодов, которым даются названия, чтобы мы могли использовать их при необходимости. Функции помогают организовывать код в простые для понимания фрагменты. Они позволяют создавать программу шаг за шагом, тестируя ее по ходу.

После того, как у Вас появится идея программы, Вы должны разработать схему ее построения - в вашей голове или на бумаге. Каждый шаг в схеме мог бы являть собой одну функцию в вашей программе. Это называется модульным программированием. Модульное программирование очень хорошо позволяет Вам скрывать детали программы, благодаря чему улучшается читабельность исходного текста вашей программы.

Например, если ваша программа содержит функцию, которая вычисляет площадь круга, следующей строкой Вы можете обратиться к ней:

$areaOfFirstCircle = areaOfCircle($firstRadius);

Глядя на подобные вызовы функций, человек, читающий исходный код вашей программы, понимает, что ваша программа делает. В то же время, ему не нужно знать, как конкретно реализована та или иная функция. Поэтому важно давать функциям "говорящие" имена - чтобы по имени можно было понять, что именно функция делает.

Вызов функции означает, что интерпретатор Perl в месте вызова функции прекращает выполнение текущей серии строк кода и переходит к выполнению кода функции. При завершении кода функции интерпретатор возвращается в точку вызова и продолжает выполнение программы со следующей строки.

Давайте присмотримся к вызову функции - сначала мы видим скалярную переменную, затем - оператор присвоения. Вы уже знаете, что это означает - Perl присвоит переменной $areaOfFirstCircle значение, стоящее справа от знака присваивания. Но что в действительности стоит справа?

Первое, что вы видите - это имя функции areaOfCircle(). Круглые скобки справа и отсутствие перед именем символов $, @ и % говорит о том, что это - вызов функции. Внутри круглых скобок содержится список параметров или значений, передающихся функции.

Вычисление площади круга:

$areaOfFirstCircle = areaOfCircle(5);
print("$areaOfFirstCircle\n");
sub areaOfCircle {
$radius = $_;
return(3.1415 * ($radius ** 2));
}

Программа напечатает:

Объявление функции:

sub имяФункции {
тело функции
}

И все. Ваша функция готова.

Сложнее дело обстоит с параметрами. Параметры - это значения, которые мы передаем в функцию. Параметры содержатся внутри круглых скобок, следующих сразу за именем функции. В примере выше вызов функции - это areaOfCircle(5). Здесь мы использовали только один параметр. Но даже в том случае, если функция имеет только один параметр, Perl при вызове функции создает массив параметров для использования их функцией.

Внутри функции массив параметров имеет имя @_. Все параметры, передаваемые функции, содержатся в массиве @_, откуда их можно извлечь при необходимости.

Наша маленькая функция из примера выше могла бы сделать это следующей строкой:

$radius = $_;

Эта строка присваивает первый элемент массива @_ скалярной переменной $radius.

Если вы хотите, вы можете не использовать в функции оператор return для возврата значения, - Perl автоматически возвратит значение последнего вычисленного выражения. Но будет лучше, если вы все же будете использовать оператор return - так вы избежите многих случайных ошибок.

Возможно вы встречали в некоторых языках программирования различия между подпрограммами и функциями. В таких языках функция обязательно возвращает значение, в то же время, подпрограмма значение не возвращает. В Perl же такого нет - у вас есть только функция - независимо от того, возвращает она какое-либо значение или нет.

Использование массива параметров (@_)

Как уже говорилось ранее - все параметры функция может найти в массиве параметров @_. Это очень удобно - чтобы узнать, сколько параметров было передано функции, нужно всего лишь обратиться к массиву параметров @_ в скалярном контексте.

firstSub(1, 2, 3, 4, 5, 6);
firstSub(1..3);
firstSub("A".."Z");
sub firstSub {
$numParameters = @_ ;
print("The number of parameters is $numParameters\n");
}

Программа напечатает:

The number of parameters is 6

The number of parameters is 3

The number of parameters is 26

Perl позволяет вам передавать в функцию любое число параметров. Функция сама может определить, какие параметры ей использовать, и в какой последовательности. Массив параметров @_ может использоваться так же, как и любой другой массив.

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

areaOfRectangle(2, 3);
areaOfRectangle(5, 6);
sub areaOfRectangle {
($height, $width) = @_ ;
$area = $height * $width;
print("The height is $height. The width is $width. The area is $area.\n\n");
}

Программа напечатает:

The height is 2. The width is 3. The area is 6.

The height is 5. The width is 6. The area is 30.

Если вы передаете в функцию не какое-либо число, а переменную, то в случае изменения ее значение внутри функции, она также изменяется и для всей остальной программы. Это называется передача параметров по ссылке.

@array = (0..5);

firstSub(@array);

sub firstSub{
$_ = "A";
$_ = "B";
}

Программа напечатает:

After fuNCtion call, array = A B 2 3 4 5

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

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

@array = (0..5);
print("Before fuNCtion call, array = @array\n");
firstSub(@array);
print("After fuNCtion call, array = @array\n");
sub firstSub{

$firstVar = "A";
$secondVar = "B";
}

Программа напечатает:

Before fuNCtion call, array = 0 1 2 3 4 5

After fuNCtion call, array = 0 1 2 3 4 5

Как вы видите - функция присваивает значения переданных ей параметров новым переменным, и оперирует в дальнейшем только ими - не изменяя непосредственно массив параметров.

Но тогда вы можете столкнуться с другой проблемой:

$firstVar = 10;
@array = (0..5);
print("Before fuNCtion call\n");

print("\tarray = @array\n");
firstSub(@array);
print("After fuNCtion call\n");
print("\tfirstVar = $firstVar\n");
print("\tarray = @array\n");
sub firstSub{
($firstVar, $secondVar) = @_ ;
$firstVar = "A";
$secondVar = "B";
}

Программа напечатает:

Before fuNCtion call
firstVar = 10
array = 0 1 2 3 4 5

After fuNCtion call
firstVar = A
array = 0 1 2 3 4 5

То есть, по умолчанию все переменные в Perl-программе доступны из любого фрагмента кода. Это очень удобно во многих случаях, но нередко доставляет неудобства и приводит к неприятным ошибкам. Далее вы узнаете, как создавать переменные, видимые только внутри соответствующих функций.

Области действия переменных.

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

Иногда бывает очень полезным ограничить область действия той или иной переменной внутри какой-либо функции. В этом случае изменение значения переменной внутри функции никак не отразится на остальных частях программы. В Perl имеются две функции - my() и local(). Первая создает переменную, которая видна только внутри данной функции. Вторая создает переменную, которую также могут "видеть" функции, вызванные из данной функции.

firstSub("AAAAA", "BBBBB");
sub firstSub{
local ($firstVar) = $_;
my($secondVar) = $_;


secondSub();
print("firstSub: firstVar = $firstVar\n");
print("firstSub: secondVar = $secondVar\n\n");
}

Sub secondSub{


$firstVar = "ccccC";
$secondVar = "DDDDD";
print("secondSub: firstVar = $firstVar\n");
print("secondSub: secondVar = $secondVar\n\n");
}

Программа напечатает:

firstSub: firstVar = AAAAA
firstSub: secondVar = BBBBB
secondSub: firstVar = AAAAA
Use of uninitialized value at test.pl line 19.
secondSub: secondVar =
secondSub: firstVar = ccccC
secondSub: secondVar = DDDDD
firstSub: firstVar = ccccC
firstSub: secondVar = BBBBB

Как вы видите, функция secondSub() не имеет доступа к переменной $secondVar, которая была создана функцией my() внутри функции firstSub(). Perl даже вывел сообщение, предупреждая вас об этом. В то же время, переменная $firstVar доступна и может быть изменена функцией secondSub().

По возможности старайтесь обходиться только функцией my() и не использовать функцию local() - так вы обеспечите себе более строгий контроль над областью действия переменных.

На самом деле, функция my() гораздо более сложна и функциональна. Но об этом пойдет речь в главе 15 - "Модули Perl".

Вы помните, в чем разница между передачей параметров функции по ссылке и по значению? Если вы передаете параметры по значению, то функция не может изменять значения переданных ей параметров (переменных), а значит это никак не отразится на всей программе. Если же вы передаете параметры по ссылке, то функция может изменять значения параметров (переменных), и это отразится на основной программе. Так вот, при использовании функции local() метод передачи параметров по ссылке работает немного по-другому - функции могут менять значения переданных им параметров (переменных), но это отразится только на самой "верхней" функции - той, где была использована функция local(), - на основную же программу это никак не повлияет.

Использование списка в качестве параметра функции.

Теперь, когда мы разобрались с областью действия переменных, давайте посмотрим на параметры функций с другой стороны. Как мы уже сказали - все параметры функции передаются в одном массиве, но что делать, если вам нужно передать в функцию один параметр - скаляр, а второй параметр - массив? Следующий пример показывает, что произойдет:

firstSub((0..10), "AAAA");
sub firstSub{
local(@array, $firstVar) = @_ ;

print("firstSub: firstVar = $firstVar\n");
}

Программа напечатает:

firstSub: array = 0 1 2 3 4 5 6 7 8 9 10 AAAA
Use of uninitialized value at test.pl line 8.
firstSub: firstVar =

Вы видите, что при инициализации переменных массив @array поглощает все элементы массива параметров @_, ничего не оставляя скаларной переменной $firstVar. Это подтверждает и предупреждающее сообщение интерпретатора Perl. Вы можете решить данную проблему путем перестановки местами параметров - если вы поставите сначала скалярную переменную, а потом массив, то все будет так как надо:

firstSub("AAAA", (0..10));
sub firstSub{
local($firstVar, @array) = @_ ;
print("firstSub: array = @array\n");
print("firstSub: firstVar = $firstVar\n");
}

Программа напечатает:

firstSub: array = 0 1 2 3 4 5 6 7 8 9 10
firstSub: firstVar = AAAA

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

Вложенные (рекурсивные) функции.

Функции могут вызывать сами себя, углубляясь на несколько уровней. На сколько - это зависит от конкретной реализации Perl, от конкретной аппаратной части и от конфигурации. Обычно, вам хватит ресурсов вашей машины, и беспокоиться об этом не стоит. Но если вам интересно, вы можете попробовать запустить вот такую программу:

FirstSub();
sub firstSub{
print("$count\n");
$count++;
firstSub();
}

Программа напечатает:

Error: Runtime exception

Перед тем, как напечатать это сообшение об ошибке, программа будет увеличивать значение переменной $count до тех пор, пока будет возможен рекурсивный вызов функции. Таким образом, по значению переменной $count вы можете узнать, сколько уровней рекурсивных вызовов допускает ваша система.

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

Частные функции.

Вам может потребоваться использовать функцию с ограниченной областью действия. Как это устраивать с переменными, мы уже разобрались. Вот как это реализуется в случае с функциями:

$temp = performCalc(10, 10);
print("temp = $temp\n");
sub performCalc {
my ($firstVar, $secondVar) = @_;
my $square = sub {
return($_ ** 2);
};
return(&$square($firstVar) + &$square($secondVar));
};

Программа напечатает:

Как вы видите, у нас имеется функция $square. Точнее, $square - это обычная скалярная переменная, ссылающаяся на функцию. Для обращения к функции мы используем знак & перед именем переменной $square. Эту функцию мы можем использовать только внутри функции performCalc() - она недоступна для остальной программы.

И снова говорю вам ЗДРАВСТВУЙТЕ! Если вы это читаете, значит продержались, к моей большой радости, до 9-го шага. Сегодня мы будем рассматривать достаточно трудные моменты языка. Если кому-то что-то будет непонятно (или было непонятно в предыдущих шагах), то пишите мне и я постраюсь объяснить. Если писем с "непонятками" на какой-то шаг наберётся определённое кол-во, то я буду переписывать этот шаг в более понятной форме. Поехали (запил кофеём)!

В Perl нет разделения на процедуры и функции, поэтому мы будем использовать общее определение - "подпрограмма". Мы научимся в этом шаге создавать свои подпрограммы, рассмотрим некоторые тонкости вызова встроеных функций Perl , коснёмся вопроса "Что такое указатель и зачем он нужен немерянно крутому программеру, которым я хочу стать?" и узнаем о пользе рукурсии в деле программерском.

Для применения подпрограммы ее необходимо определить либо в текущем модуле (файле), либо во внешнем модуле (файле). Объявляются подпрограммы с помощью ключевого слова sub . Чтобы использовать подпрограмму объявленную во внешнем модуле, нужно перед её использованием указать интерпретатору где её искать следющим образом use имя_модуля qw(подрограмма) или require имя_файла . Как происходит вызов подпрограмм посмотрим на примере

#!/usr/bin/perl -w sub test { print "Привет из подпрограммы!\n"; } &test();

Cимвол & означает, что после него идёт вызов подпрограммы. Т.е. в данном случаем вызов подпрограммы test() . Но можно даже обойтись вообще без него. Теперь ещё одна тонкость. Обойтись можно и без скобок. Выглядеть это будет так:

#!/usr/bin/perl -w sub test { print "Привет из подпрограммы!\n"; } test;

Причем обойтись без скобок можно не только в тех подпрограммах, которые создаём мы, но и при вызове функций Perl . Скобки нужны только в том случае, если передаваемые функции параметры могут вызвать неоднозначность трактования. Мы уже не раз "своевольничали", не пользуясь скобками. Функция print правильно пишется так - print("Привет из подпрограммы!\n") . Теперь о том, как передаются подпрограммам параметры. Каждая подпрограмма при запуске получает массив @_ , в котором содержатся параметры. Ещё один пример:

#!/usr/bin/perl -w sub test { print $_; } &test("Привет из подпрограммы!\n");

Или так:

#!/usr/bin/perl -w sub test { print $_; } test "Привет из подпрограммы!\n";

Ощутили? Поехали дальше:) Подпрограммы могут быть объявленны как перед их вызовом, так и после. Подпрограммы могут возвращать какое-то значение. Для этого служит оператор return , он возвращает идущее за ним значение.

#!/usr/bin/perl -w sub test { return "Я вернулся:)"; } print &test();

Можно обходится и без него. Тогда Perl вернёт значение последнего оцененного выражения. Ещё о передаче подпрограмме параметров. В Perl все переменные передаются по ссылке. Поэтому нет смысла передавать подпрограмме указатель на переменную. При передаче параметром массива, мы фактически передаём ту же ссылку на первый элемент массива, поэтому можно так же, как с переменной, не указывать специально, что мы передаём указатель. Но я всё же покажу как это делается. Указатель - это переменная содержащая адрес другой переменной. Чтобы объявить указатель надо перед переменной поставить символ * . Примерно так:

*POINTER;

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

#!/usr/bin/perl -w $x=0; print \$x;

Как видите мы получили не содержимое переменной, а её адрес - 0x18326bc (у вас может быть другой).

А сейчас рассмотрим как вынести свои подпрограммы в отдельный файл. Для этого достаточно сохранить свою подпрограмму в файл (предположим "test.pl"), а в том файле, в котором стоит вызов подпрограммы написать require "test.pl" . Как делать модули мы узнаем позже.

Обещаная рекурсия. Рекурсивной называется подпрограмма, способная вызывать саму себя. Что это даёт? Например, возможность обработки древовидных структур, как то - дерево каталогов, ссылочная связь гипертекстовых документов и т.п. Ни с сетью, ни с файлами мы пока работать не умеем. Научимся чуть позже. Поэтому пока будем эмулировать бурную деятельность:)

#!/usr/bin/perl -w $cnt=0; sub recurs { my $level = $_; $cnt++; print "$level\n"; if($cnt

Встпвляет пространство имен одного модуля в другой. Это не встроенная функция, а всего лищь метод, наследуемый от модуля (параметр МОДУЛЬ), которому необходимо экспортировать свои имена (параметр СПИСОК) в другой модуль.

Import МОДУЛЬ СПИСОК

local

Функция local() используется для объявления и инициализации одной или нескольких переменных:

Local EXPR local ($myvar, , %myhash); local $pi = 3.14159; local ($pi, $exp) = (3.14159, 2.71828);

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

  • подпрограммы;
  • заключенного в фигурные скобки блока операторов;
  • выражения, пареданного на выполнение функции eval();
  • файла;

В зависимости от того, в каком месте вызвана для объявления переменных сама функция local(). Если функция local() применяется для описания нескольких переменных, они должны быть заключены в скобки. Если глобальня перемнная, объявленная при помощи этой функции, ранее встречалась до объявления и имела некоторое значение, то это значение сохраняется в скрытом стеке и восстанавливается после выхода соответственно из подпрограммы, блока, функции eval() или файла. Переменная, объявленная при помощи функции local(), или, точнее, ее временное значение, доступна для любой функции, вызванной внутри подпрограммы, блока, функции eval() или файла, в которых сделано объявление. Такую переменную называют динамической, а ее область видимости - динамической областью видимости. В названии отражается тот факт, что область видимости переменной динамически изменяется с каждым вызовом функции, получающей доступ к этой переменной.

Пример:

my

Функция my() используется для объявления одной или нескольких переменных локальными:

и ограничивает их область действия:

  • подпрограммой;
  • заключенным в фигурные скобки блоком операторов;
  • выражением, пареданным на выполнение функции eval();
  • файлом, в зависимости от того, в каком месте вызвана для объявления переменных сама функция my().

Если выражение EXPR содержит список переменных, то он должен быть заключен в скобки:

My ($myvar, @mylist, %myhash);

Одновременно с объявлением переменные могут быть инициализированны:

My $pi = 3.14159; my ($pi, $exp) = (3.14159, 2.71828);

Переменные, объявленные при помощи функции my(), доступны в своей области действия только для подпрограмм, определенных в этой области. Для подпрограмм, определенных за ее пределами, они недоступны. Такие переменные называются лексическими, а саму область видимости - лексической или статической областью видимости.

Пример:

Sub f1{ local ($x) = "aaaa"; my($y) = "bbbb"; print("f1: x = $xn"); print("f1: y = $ynn"); f2(); print("f1: x = $xn"); print("f1: y = $ynn"); } sub f2{ print("f2: x = $xn"); print("f2: y = $ynn"); $x = "cccc"; $y = "dddd"; print("f2: x = $xn"); print("f2: y = $ynn"); } f1;

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

F1: x = aaaa f1: y = bbbb f2: x = aaaa f2: y = f2: x = cccc f2: y = dddd f1: x = cccc f1: y = bbbb

Как видно из приведенного результата, функция f2() не имеет доступа к переменной $y, объявленной при помощи функции my() внутри функции f1(), и,напртив, имеет доступ к переменной $x, объявленной внутри f1() при помощи функции local().

package

Определяет отдельное глобальное пространство имен (пакет): все неопределенные динамические идентификаторы (включая те, которые объявлены через local(), но не my()) будут храниться в нем. Для доступа к ним вне пакета следует указывать префикс, представляющий имя пакета с последующими двумя символами двоеточий "::". Область видимости переменных пакета распространяется до конца блока операторов, в котором расположен пакет или до нового объявления пакета. Если опущено имя пакета, то предписывает, чтобы все индентификаторы были определены, включая имена функций.

Package [ИМЯ_ПАКЕТА]

use

Загружает модуль во время компиляции; если модуль не доступен, то компиляция всей программы прекращается.

Use МОДУЛЬ СПИСОК use МОДУЛЬ use МОДУЛЬ ВЕРСИЯ СПИСОК use ВЕРСИЯ