2012-03-06 3 views
3

Мне нужна подпрограмма, которая полностью удаляет элемент массива на месте. Не удалось выполнить следующий код:Perl: splice() array using подпрограмма

sub del 
{ 
    splice(@_,2,1); 
} 

@array=(0..5); 
print "@array"."\n"; 
del(@array); 
print "@array"."\n"; 

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

ответ

14

В то время как скалярные элементы @_ являются псевдонимом передаваемых данных, @_ сам по себе является другой переменной. Это означает, что $_[1] = "foo" изменит $_[1], но push @_, "foo" не изменит @_. В противном случае my $self = shift был бы Bad Thing.

Вам необходимо передать массив в качестве ссылки.

sub del { 
    my $array_ref = shift; 

    splice @$array_ref, 2, 1; 

    return; 
} 

del \@array; 

Если вы абсолютно необходимо сохранить интерфейс del @array, это один из немногих мест, где это уместно использовать прототип.

sub del(\@) { 
    my $array_ref = shift; 

    splice @$array_ref, 2, 1; 

    return; 
} 

del @array; 

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

Что еще более важно, это делает неочевидным то, что del изменит свои аргументы. Обычно пользовательские функции Perl копируют свои аргументы, поэтому вы можете посмотреть foo @array и быть достаточно уверенными. @array не будет изменен foo. Это позволяет быстро скопировать код для вещей, которые будут влиять на переменную. Эталонный прототип выдает это окно. Теперь каждая функция должна быть проверена на возможную скрытую перекрестную ссылку.

+0

s/копия/другая переменная / – ikegami

1

Ответ может быть найден с perldoc perlsub:

Любые аргументы, переданные в Обнаруживаются в массиве @. Поэтому, если вы называете функцию с двумя аргументами, они будут храниться в $ [0] и $ [1]. Массив @ является локальным массивом, , но его элементы являются псевдонимами для реальных скалярных параметров. В частности, если обновлен элемент $ [0], обновляется соответствующий аргумент (или возникает ошибка, если он не обновляется ). Если аргумент представляет собой массив или хэш-элемент, который не существовал при вызове функции, этот элемент создается только тогда, когда (и если) он изменен или ссылка на взята. (Некоторые более ранние версии Perl создали элемент, независимо от того, был ли назначен элемент.) Присвоение целому массиву @ удаляет этот псевдоним и не содержит .

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

Смежные вопросы