Листинг 9.3. Использование функции mу( ) для передачи параметров по значению
#!/usr/bin/perl
# Передача в подпрограмму параметров по значению
sub f{
my($x, $y) = @_;
return(++$x * --$y);
}
$val = f(9,11);
print "Значение (9+1) * (11-1) равно $val\n";
$x = 9;
$y = 11;
$val = f($x,$y);
print "Значение ($x+1) * ($y-1) равно $val\n";
print "Значение \$х остается равным $x, a \$y равным $y\n";
Результат выполнения:
Значение (9+1) * (11-1) равно 100.
Значение (9+1) * (11-1) равно 100.
Значение $x остается равным 9, а $у равным 11.
Передача по ссылке параметров-массивов
Итак, подпрограмма получает и возвращает параметры через специальный массив @_. Если параметр
является массивом или хэш-массивом, его элементы также сохраняются в массиве параметров @_. При передаче
в подпрограмму нескольких параметров-массивов или хэш-массивов они утрачивают свою целостность. Иными
словами, после записи параметров-массивов (хэш-массивов) в массив @_ из него невозможно выделить
отдельный параметр-массив (хэш-массив): все параметры в массиве @_ хранятся единой «кучей». Чтобы
сохранить при передаче в подпрограмму целостность массива или хэш-массива, следует использовать один
из двух основных существующих подходов.
Использование типа typeglob
Первый, более старый, заключается в использовании внутреннего типа данных, называемого typeglob.
Принадлежность к типу typeglob обозначается префиксом *. Префикс * можно рассматривать как метасимвол,
вместо которого может стоять любой из префиксов $, @, %, &, обозначающих тип данных «скаляр», «массив»,
«хэш-массив», «функция» соответственно. Интерпретатор преобразует переменную типа typeglob, например
*abc, в скалярную величину, которая является ссылкой на гнездо в таблице символов, содержащее элементы
разных типов с одинаковым именем abc, и представляет любой из этих элементов. Например, запись *аbс
обозначает всю совокупность, а также любую из следующих переменных: скаляр $abc, массив @abc,
хэш-массив %abc, функция &abc.
Передача в подпрограмму вместо параметра-массива или хэш-массива соответствующей переменной типа
typeglob является имитацией передачи параметра-массива (хэш-массива) по ссылке с сохранением его
целостности. Рассмотрим пример из листинга 9.4.
Листинг 9.4. Использование типа typeglob для передачи параметров массивов и хэш- массивов
sub doublargs {
local (*mylist, *myhash) = @_;
foreach $item (@mylist) {
$item *= 2;
}
foreach $key (keys %myhash) {
$myhash{$key} *= 2;
}
}
@somelist = (1,2,3);
%somehash = ("one" => 5, "two" => 15, "three" => 20);
print "начальные значения: \n\@somelist = @somelist\n";
foreach $key (keys %somehash) {
print "\$somehash{$key} = $somehash{$key}";
}
print "\n";
doublargs(*somelist,*somehash);
print "итоговые значения:\n\@somelist = @somelist\n";
foreach $key (keys %somehash) {
print "\$somehash{$key} = $somehash{$key}";
}
print "\n";
Подпрограмма doublargs принимает на вход массив и хэш-массив и изменяет их элементы, умножая на 2.
Вместо массива и хэш-массива в подпрограмму передаются соответствующие переменные типа typeglob, которые
легко выделить из массива @_, так как фактически они являются скалярами. Обратите внимание на применение
функции
local.
Использовать вместо нее функцию
my( )
здесь нельзя, так как переменная типа typeglob не
может быть локальной, она представляет несколько одноименных переменных разных типов из таблицы символов.
Далее возникает вопрос, каким образом изменение в подпрограмме массива @mylist влияет на изменение
фактического параметра @somelist. Дело в том, что операция присваивания вида *х = *у создает синоним *х
для гнезда таблицы символов *у, так что осуществление операции над $х, @х, %х эквивалентно осуществлению
этой операции над $у, @y, %у. В результате присваивания
local(*mylist, *myhash) = @_;
создается псевдоним *mylist для *somelist, поэтому все изменения элементов массива @mylist внутри
подпрограммы эквивалентны изменениям элементов массива @somelist. Все сказанное справедливо и для
хэш-массивов %myhash и %somehash. Результат подтверждает корректность передачи массива и хэш-массива
по ссылке:
Начальные значения:
@somelist = l 2 3
$somehash{one} = 5
$somehash{three} = 20
$somehash{two} = 15
Итоговые значения:
@somelist = 2 4 6
$somehash{one} = 10
$somehash{three} = 40
$somehasn{two} = 30
Использование ссылок
Второй способ передачи массивов в подпрограмму заключается в том, чтобы вместо собственно массивов
или хэш-массивов передавать ссылки на них. Ссылка является скалярной величиной, и ее легко выделить в
массиве параметров @_. Внутри подпрограммы остается только применить к ссылке операцию разыменования
для того, чтобы получить доступ к фактическому параметру. Поскольку ссылки появились только в версии
Perl 5, то этот способ является относительно новым. При помощи ссылок предыдущий пример можно записать
в таком виде, как показано в листинге 9.5.
Листинг 9.5. Использование ссылок для передачи параметров-массивов и хэш-массивов
sub doublparms {
my ($listref, $hashref) = @_;
foreach $item (@$listref) {
$item *= 2;
}
foreach $key (keys %$hashref) {
$$hashref{$key} *= 2;
}
}
@somelist = (l,2,3);
%somehash=("one" => 5, "two" => 15, "three" => 20);
print "начальные значения:\@somelist = @somelist\n";
foreach $key (keys %somehash) {
print "\$somehash{$key} = $somehash{$key}";
}
print "\n";
doublparms(\@somelist,\%somehash);
print "итоговые значения:\n\@somelist = @somelist\n";
foreach $key (keys %somehash) {
print "\$somehash{$key} = $somehash{$key}";
}
print "\n";
Здесь для описания локальных переменных использована функция my( ). Как мы выяснили ранее в этой
главе, применение функции my( ) в подобном случае реализует передачу параметров по значению, то есть
их изменение внутри подпрограммы не влияет на фактические параметры. Каким же образом в данном случае
осуществляется передача массива и хэш-массива по ссылке? Дело в том, что по значению передаются только
ссылки, указывающие на фактические параметры: массив @somelist и хэш-массив %somehash. Используя операции
разыменования внутри подпрограммы, мы получаем доступ непосредственно к массиву @somelist и
хэш-массиву %somehash и изменяем их элементы. В результате выполнения данного сценария будет выведено:
Начальные значения:
@somelist = 1 2 3
$somehash{one} = 5
$samehash{three} = 20
$somehash{two} = 15
Итоговые значения:
@somelist = 2 4 6
$samehash{one} = 10
$somehash{three} = 40
$somehash{two} = 30