В Perl подпрограммы принимают аргументы через специальную переменную-массив @_, куда автоматически помещаются все переданные параметры. Особенность Perl — параметры попадают в @_ по ссылке, а значит, изменение элементов внутри функции меняет их значения снаружи.
Чтобы избежать неожиданного изменения переданных данных, параметры рекомендуется копировать в переменные:
sub sum { my ($a, $b) = @_; return $a + $b; }
Перловые функции всегда возвращают список значений, и результат функции — это последний вычисленный список или явный оператор return.
sub minmax { my ($x, $y) = @_; return ($x < $y ? $x : $y, $x > $y ? $x : $y); } my ($min, $max) = minmax(4, 7); # $min = 4; $max = 7
Если вызвать функцию в скалярном контексте — она вернет количество возвращённых элементов, если не заключать результат в скаляр.
Как передать большое количество переменных в функцию Perl, чтобы случайно не изменить значения исходных переменных?
Бессознательно отвечают: "Нужно просто копировать аргументы в переменные так же, как обычно!"
Правильный ответ:
Копирование работает лишь для скаляров. Если вы работаете с массивами или хэшами, используйте ссылки, чтобы управлять передачей данных явно и избегать копий по ссылке:
sub modify { my ($arr_ref) = @_; push @$arr_ref, 'new'; # изменяет исходный массив } my @data = (1, 2, 3); modify(\@data); # теперь @data = (1, 2, 3, 'new')
История
История 1
В крупном скрипте по обработке отчётов внутренняя функция меняла значения элементов массива, переданного из основной программы. В результате, после выполнения функции, исходные данные были искажены. Ошибка заключалась в том, что автор функции не делал копии входных значений, полагая, что изменения будут локальными, — а это неверно для @_ с массивами.
История 2
При миграции скриптов из Perl 4 (где переменные копировались иначе) команда столкнулась с проблемой: передача вложенных хэшей приводила к неожиданной "утечке" данных между вызовами подпрограмм — модификация полей хэша в одной функции влияла на другие части кода. Причина — ссылка на хэш в списке @_.
История 3
Разработчик реализовал функцию, возвращающую массив результатов, но в вызывающем месте использовал скалярный контекст:
$count = func(@params);. Ожидалось, что переменной присвоится значение первого возвращённого элемента, а на деле в$countоказалось количество элементов в списке, что привело к задержкам в расчётах и путанице.