Какая лучшая практика 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);

Что мне не хватает?

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