Какая лучшая практика Perl для возврата хэшей из функций?
Я размышляю над лучшей практикой передачи хеш-ссылок для возвращаемых данных в/из функций.
С одной стороны, кажется интуитивным передавать только входные значения функции и иметь только возвращаемые выходные переменные. Тем не менее, передача хэшей в Perl может быть выполнена только по ссылке, поэтому она немного беспорядочна и может показаться скорее возможностью совершить ошибку.
Другим способом является передача ссылки во входных переменных, но тогда ее нужно решать в функции, и может быть неясно, что такое ввод, а какая - возвращаемая переменная.
Какова наилучшая практика в этом отношении?
Возвращает ссылки на массив и хэш, а затем разыгрывает его.
($ref_array,$ref_hash) = $this->getData('input');
@array = @{$ref_array};
%hash = %{$ref_hash};
Передайте ссылки (@array,% hash) функции, которая будет содержать выходные данные.
$this->getData('input', \@array, \%hash);
Ответы
Ответ 1
Просто верните ссылку. Нет необходимости разыменовывать все
хэш, как вы делаете в своих примерах:
my $result = some_function_that_returns_a_hashref;
say "Foo is ", $result->{foo};
say $_, " => ", $result->{$_} for keys %$result;
и др.
Я никогда не видел, чтобы кто-либо проходил в пустых ссылках, чтобы провести результат. Это Perl, а не C.
Ответ 2
Попытка создать копии, сказав
my %hash = %{$ref_hash};
еще более опасен, чем использование hashref. Это связано с тем, что он создает только мелкую копию. Это приведет вас к мысли, что можно изменить хэш, но если он содержит ссылки, они изменят исходную структуру данных. Я считаю, что лучше просто передавать ссылки и быть осторожными, но если вы действительно хотите убедиться, что у вас есть копия переданной ссылки, вы можете сказать:
use Storable qw/dclone/;
my %hash = %{dclone $ref_hash};
Ответ 3
Первый лучше:
my ($ref_array,$ref_hash) = $this->getData('input');
Причины:
- во втором случае getData() должен
проверьте структуры данных, чтобы сделать
уверены, что они пусты.
- у вас есть свобода вернуть undef в качестве специального значения
- он выглядит более Perl-идиоматическим.
Примечание: строки
@array = @{$ref_array};
%hash = %{$ref_hash};
сомнительны, так как вы мелко копируете все структуры данных здесь. Вы можете использовать ссылки везде, где вам нужен массив/хеш, используя → оператор для удобства.
Ответ 4
Если это становится настолько сложным, что и callsite, и вызываемая функция платят за него (потому что вы должны думать/писать больше каждый раз, когда используете его), почему бы просто не использовать объект?
my $results = $this->getData('input');
$results->key_value_thingies;
$results->listy_thingies;
Если сделать объект "слишком сложным", тогда начните использовать Moose, чтобы он больше не был.
Ответ 5
Мои личные предпочтения для вспомогательных интерфейсов:
- Если в подпрограмме есть аргументы 0-3, они могут быть переданы в виде списка:
foo( 'a', 12, [1,2,3] );
- В противном случае передайте список пар значений имени.
foo( one => 'a', two => 12, three => [1,2,3] );
- Если подпрограмма имеет или может иметь более одного аргумента, серьезно рассмотреть использование пар имя/значение.
Передача ссылок увеличивает риск непреднамеренной модификации данных.
В случае возврата я обычно предпочитаю возвращать список результатов, а не массив или хеш-ссылку.
Я возвращаю хеш или массив refs, когда он будет заметно улучшать скорость или потребление памяти (т.е. BIG-структуры) или когда задействуется сложная структура данных.
Возвращая ссылки, когда это не требуется, лишает одну из возможностей использовать функции управления списком Perl nice и подвергает их опасности непреднамеренной модификации данных.
В частности, мне полезно назначить список результатов в массив и вернуть массив, который обеспечивает контекстное поведение возврата массива на мои субтитры.
Для случая прохождения в двух хэшах я бы сделал что-то вроде:
my $foo = foo( hash1 => \%hash1, hash2 => \%hash2 ); # gets number of items returned
my @foo = foo( hash1 => \%hash1, hash2 => \%hash2 ); # gets items returned
sub foo {
my %arg = @_;
# do stuff
return @results;
}
Ответ 6
Я изначально разместил это на другом вопросе, а затем кто-то указал на это как на "связанный пост", поэтому я отправлю его здесь, чтобы поступить по этому вопросу, предполагая, что люди встретятся с ним в будущем.
Я буду противоречить принятому ответу и скажу, что предпочитаю, чтобы мои данные возвращались как простой хеш (ну, как список размером по размеру, который может быть интерпретирован как хэш). Я работаю в среде, где мы склонны делать такие вещи, как следующий фрагмент кода, и гораздо проще комбинировать и сортировать, а также кусочки и кусочки, когда вам не нужно разыгрывать каждую другую строку. (Также приятно знать, что кто-то не может повредить ваш hashref, потому что вы передали всю вещь по стоимости - хотя кто-то указал, что если ваш хэш содержит больше, чем просто скаляры, это не так просто.)
my %filtered_config_slice =
hashgrep { $a !~ /^apparent_/ && defined $b } (
map { $_->build_config_slice(%some_params, some_other => 'param') }
($self->partial_config_strategies, $other_config_strategy)
);
Это приблизительно соответствует тому, что может сделать мой код: построение конфигурации для объекта на основе различных объектов стратегии конфигурации (некоторые из которых объект знает по существу, плюс дополнительный парень), а затем отфильтровывают некоторые из них как несущественные.
(Да, у нас есть хорошие инструменты, такие как hashgrep
и hashmap
и lkeys
, которые полезны для хэшей. $a и $b получают ключ и значение каждого элемента в списке соответственно). (Да, у нас есть люди, которые могут программировать на этом уровне. Наем является неприятным, но у нас есть качественный продукт.)
Если вы не собираетесь делать что-либо похожее на функциональное программирование, подобное этому, или если вам нужна большая производительность (вы профилировали?), тогда обязательно используйте hashrefs.
Ответ 7
Uh... "передача хэшей может быть выполнена только по ссылке"?
sub foo(%) {
my %hash = @_;
do_stuff_with(%hash);
}
my %hash = (a => 1, b => 2);
foo(%hash);
Что мне не хватает?
Я бы сказал, что если проблема заключается в том, что вам нужно иметь несколько выходов из функции, лучше, чем обычно, выводить структуру данных, возможно, хеш, которая содержит все необходимое для отправки, а не для модификации ссылки как аргументы.