Какая лучшая практика Perl для возврата хэшей из функций?

Я размышляю над лучшей практикой передачи хеш-ссылок для возвращаемых данных в / из функций.

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

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

Какая лучшая практика в этом отношении?

Вернуть ссылки на массив и хэш, а затем разыменовать его.

($ref_array,$ref_hash) = $this->getData('input');
@array = @{$ref_array};
%hash = %{$ref_hash};

Передайте ссылки (@array,% hash) функции, которая будет хранить выходные данные.

$this->getData('input', \@array, \%hash);

person Dan Littlejohn    schedule 07.07.2009    source источник


Ответы (7)


Просто верните ссылку. Нет необходимости разыменовать весь хеш, как в своих примерах:

my $result = some_function_that_returns_a_hashref;
say "Foo is ", $result->{foo};
say $_, " => ", $result->{$_} for keys %$result;

и Т. Д.

Я никогда не видел, чтобы кто-нибудь передавал пустые ссылки, чтобы сохранить результат. Это Perl, а не C.

person jrockway    schedule 07.07.2009
comment
А как насчет GetOpt :: Std. Вы можете передать пустую ссылку на хэш в качестве второго аргумента функции getopts (a: b: c :, \% hash_ref) для заполнения ссылки на хеш для доступа, например, $ hash_ref {opt_a} и т. Д. Не говоря уже о том, что это красиво, но библиотеки Perl реализовали это. - person Chris Serra; 11.03.2011

Пытаюсь создать копии, говоря

my %hash = %{$ref_hash};

даже опаснее, чем использование hashref. Это потому, что он создает только неглубокую копию. Это приведет вас к мысли, что изменять хеш - это нормально, но если он содержит ссылки, они изменят исходную структуру данных. Я считаю, что лучше просто передавать ссылки и быть осторожными, но если вы действительно хотите убедиться, что у вас есть копия переданной ссылки, вы можете сказать:

use Storable qw/dclone/;

my %hash = %{dclone $ref_hash};
person Chas. Owens    schedule 07.07.2009
comment
Хорошее дополнение к обсуждению. Я думаю, что эта ошибка распространена, потому что быстрый тест на чем-то простом, например, на одномерном массиве, кажется, работает. Тогда ppl запутается, что он не распространяется на более сложные структуры, такие как хэши и массивы N-D, N ›1. - person SSilk; 06.09.2017

Первый лучше:

my ($ref_array,$ref_hash) = $this->getData('input');

Причины:

  • во втором случае getData () необходимо проверить структуры данных, чтобы убедиться, что они пусты.
  • у вас есть свобода вернуть undef как особое значение
  • это выглядит более Perl-идиоматичным.

Примечание: линии

@array = @{$ref_array};
%hash = %{$ref_hash};

сомнительны, так как вы неглубоко копируете здесь целые структуры данных. Вы можете использовать ссылки везде, где вам нужен массив / хэш, используя для удобства оператор ->.

person Igor Krivokon    schedule 07.07.2009
comment
Причина, по которой я делал разыменование в прошлом, состоит в том, чтобы сделать его более читаемым, чтобы вам не приходилось пытаться выяснить, какой тип ссылки он возвращает. - person Dan Littlejohn; 08.07.2009
comment
в этом случае я рекомендую вам вместо этого сделать local (*array, *ref) = $this->getData('input'). таким образом вы избегаете копирования и получаете переменные @array и %hash в качестве псевдонимов для возвращаемых ссылок. Но будьте осторожны, вы все равно потеряете сигил при доступе к члену, потому что вы получите $array[1] и $hash{a}. Я придерживался бы $array_ref->[1] и _7 _... - person Massa; 08.07.2009

Если это становится настолько сложным, что за него платят как вызывающий сайт, так и вызываемая функция (потому что вам нужно думать / писать больше каждый раз, когда вы его используете), почему бы просто не использовать объект?

my $results = $this->getData('input');

$results->key_value_thingies;
$results->listy_thingies;

Если создание объекта «слишком сложно», начните использовать Moose, чтобы его больше не было.

person Community    schedule 08.07.2009
comment
м-м-м, объекты в Perl всегда были наполовину задницей. Идея разумная, но попытка использовать это для большого набора данных для скорости не работает. - person Dan Littlejohn; 11.07.2009
comment
если вы действительно думаете, что это медленно, используйте вместо этого C ++. Perl медленный по многим другим причинам, создание экземпляра объекта не является одной из них. - person ; 14.07.2009

Мое личное предпочтение субинтерфейсов:

  1. Если подпрограмма имеет 0–3 аргумента, они могут быть переданы в виде списка: foo( 'a', 12, [1,2,3] );
  2. В противном случае передайте список пар имя-значение. foo( one => 'a', two => 12, three => [1,2,3] );
  3. Если подпрограмма имеет или может иметь более одного аргумента, серьезно подумайте об использовании пар имя / значение.

Передача ссылок увеличивает риск непреднамеренного изменения данных.

При возврате я обычно предпочитаю возвращать список результатов, а не ссылку на массив или хэш.

Я возвращаю хэш или ссылки на массив, когда это приведет к заметному увеличению скорости или потребления памяти (например, БОЛЬШИЕ структуры) или когда задействована сложная структура данных.

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

В частности, я считаю полезным назначить список результатов в массив и вернуть массив, который обеспечивает контекстное поведение возврата массива моим подпрограммам.

В случае передачи двух хешей я бы сделал что-то вроде:

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;
}
person daotoad    schedule 07.07.2009

Первоначально я разместил это в другом вопросе, а затем кто-то указал на это как на «связанный пост», поэтому я размещу его здесь, чтобы высказать свое мнение по этому вопросу, предполагая, что люди столкнутся с этим в будущем.

Я собираюсь опровергнуть принятый ответ и сказать, что я предпочитаю, чтобы мои данные возвращались в виде простого хэша (ну, в виде списка равного размера, который, вероятно, будет интерпретирован как хеш). Я работаю в среде, где мы, как правило, делаем такие вещи, как следующий фрагмент кода, и гораздо проще комбинировать, сортировать, нарезать и нарезать кубиками, когда вам не нужно разыменовывать все остальные строки. (Также приятно знать, что кто-то не может повредить ваш 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.

person Community    schedule 06.01.2010

Эээ ... "передача хэшей может быть выполнена только по ссылке"?

sub foo(%) {
    my %hash = @_;
    do_stuff_with(%hash);
}

my %hash = (a => 1, b => 2);
foo(%hash);

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

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

person chaos    schedule 07.07.2009
comment
Я почти уверен, что когда вы называете это таким образом, он съедает любые аргументы после хеша и добавляет их к самому хешу. - person Eric; 08.07.2009
comment
Как это отвечает на вопрос? Это может быть комментарий, а не ответ. - person Igor Krivokon; 08.07.2009
comment
в любом случае прототипы - плохая практика. - person Dan Littlejohn; 08.07.2009
comment
@Eric: Нет, прототип (%) дает ему особые возможности. Если вы изменили его на sub foo(\%\@), вы могли бы вызвать foo(%hash, @array) и вытащить хэш-ссылку и ссылку на массив внутри подпрограммы. - person ephemient; 08.07.2009