Каков самый эффективный способ выполнить ниже? (Я знаю, что они выполняют то же самое, но как большинство людей сделало бы это между этими тремя, и почему?)
my %hash = build_hash();
# Do stuff with hash using $hash{$key}
sub build_hash
{
# Build some hash
my %hash = ();
my @k = qw(hi bi no th xc ul 8e r);
for ( @k )
{
$hash{$k} = 1;
}
# Does this return a copy of the hash??
return %hash;
}
my $hashref = build_hash();
# Do stuff with hash using $hashref->{$key}
sub build_hash
{
# Build some hash
my %hash = ();
my @k = qw(hi bi no th xc ul 8e r);
for ( @k )
{
$hash{$k} = 1;
}
# Just return a reference (smaller than making a copy?)
return \%hash;
}
my %hash = %{build_hash()};
# Do stuff with hash using $hash{$key}
# It is better, because now we don't have to dereference our hashref each time using ->?
sub build_hash
{
# Build some hash
my %hash = ();
my @k = qw(hi bi no th xc ul 8e r);
for ( @k )
{
$hash{$k} = 1;
}
return \%hash;
}
Я предпочитаю вернуть хэш-файл по двум причинам. Первая - он использует немного меньше памяти, так как нет копии. Вторая, она позволяет делать это, если вам нужен только один кусочек хэша.
my $value = build_hash()->{$key};
Научитесь любить хэш-ссылки, вы будете часто их видеть, как только начнете использовать объекты.
a. pl
и c.pl
требуют, чтобы была взята копия хэша (а внутренний хэш функции помечен как свободная память). С другой стороны, b.pl
строит хэш всего один раз и требует немного дополнительной памяти для возврата ссылки, по которой вы можете работать. Таким образом, b.pl
, скорее всего, является наиболее эффективной формой тройки, как в пространстве, так и во времени.
Я собираюсь пойти против зерна и того, что говорят все остальные, и сказать, что я предпочитаю, чтобы мои данные возвращались в виде гашиша (ну, в виде чётного списка, который, скорее всего, будет интерпретирован как гашиш). Я работаю в среде, где мы обычно делаем такие вещи, как следующий фрагмент кода, и гораздо проще комбинировать и сортировать, а также нарезать кубики и кубики, когда не нужно разыменовывать каждую другую строку. (Также приятно знать, что кто-то не может повредить ваш хэш-файл, потому что вы передали все по значению. edit: unless you've got references to other objects/hashes/arrays in the hash-values, then you're in trouble anyway).
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)
);
This approximates something that my code might do: building a configuration for a object based on various configuration strategy objects (some of which the object knows about inherently, plus some extra guy) and then filters out some из них as irrelevant.
(Да, у нас есть хорошие инструменты вроде hashgrep
и hashmap
и lkeys
, которые делают полезные вещи с хэшами. $a и $b устанавливаются на клавишу и значение каждого элемента списка соответственно). (Да, у нас есть люди, которые могут программировать на этом уровне. Найм - это неприятно, но у нас есть качественный продукт.)
Если вы не собираетесь делать ничего подобного, похожего на функциональное программирование, или если вам нужно больше производительности (вы профилировали?), то конечно, используйте хэши.
(Да, у нас есть люди, которые могут программировать на этом уровне.Относительно возвращения гашиша из функционировать, как правило, вы должны вернуться сам гашиш, а не ссылка. Вы может быть использована ссылка, если хэш Огромный, и память или время - это проблема, но это не должно быть твоим первым беспокойством. -- ...заставить код работать.
Мне придется не согласиться с Эфиром. Было время, когда я занимал эту позицию, но быстро оказался в аду, когда должен был вспомнить, какие sub
s возвращали хэши и какие возвращали хэши, что было довольно серьёзным препятствием для простого заставления кода работать. Важно стандартизировать либо всегда возвращающий хэш/массив, либо всегда возвращающий хэш/массив, если только вы не хотите постоянно спотыкаться о себя.
Что касается стандартизации, то я вижу несколько преимуществ использования ссылок:
Когда вы возвращаете хэш или массив, то на самом деле вы возвращаете список, содержащий сплющенную копию оригинального хэша/массива. Так же как и передача параметров хэша/массива в sub
, это имеет недостаток, заключающийся в том, что вы можете посылать только один список за раз. Конечно, не часто нужно возвращать несколько списков значений, но это случается, так зачем же выбирать стандартизацию, которая исключает это?
Преимущества (обычно пренебрежимо малы) производительности/памяти при возврате одного скаляра, а не потенциально гораздо большего куска данных.
Она поддерживает согласованность с кодом OO, который часто передает объекты (т.е. благословенные ссылки) туда и обратно.
Если по какой-либо причине важно, чтобы у вас была свежая копия хэша/массива, а не ссылка на оригинал, код вызова может легко сделать такую копию, как показано в c.pl
. Однако, если вы вернете копию хэша, то вызывающий абонент не сможет превратить ее в ссылку на оригинал. (В случаях, когда это выгодно, функция может сделать копию и вернуть ссылку на копию, таким образом защищая оригинал и в то же время избегая "this возвращает хэши, , который возвращает хэши", ад, о котором я упоминал ранее)
Как упоминал Шверн, очень приятно иметь возможность сделать мой $foo = $obj->some_data->{key}
.
Единственное преимущество, которое я могу видеть в всегда возвращающих хэши/массивы - это то, что это проще для тех, кто не понимает ссылок или кому неудобно работать с ними. Учитывая, что комфорт с рекомендациями развивается за несколько недель или месяцев, за которыми следуют годы или десятилетия беглой работы с ними, я не считаю это значимым преимуществом.
Почему бы не вернуть оба? Контекст - это очень мощная функция в Perl, позволяющая вашим функциям "делать то, что вы имеете в виду". Часто решение о том, какое значение возврата лучше, зависит от того, как код вызова планирует использовать это значение, именно поэтому в Perl есть встроенный wantarray
.
sub build_hash {
my %hash;
@hash{@keys} = (1) x @keys;
wantarray ? %hash : \%hash
}
my %hash = build_hash; # list context, a list of (key => value) pairs
my $href = build_hash; # scalar context, a hash reference
Я бы вернул ссылку, чтобы сэкономить время обработки сглаживания хэша в список скаляров, построения нового хэша и (возможно) сбора местного мусора в подпрограмме.
Вы ищете кусочек хэша :
# assigns the value 1 to every element of the hash
my %hash; # declare an empty hash
my @list = qw(hi bi no th xc ul 8e r); # declare the keys as a list
@hash{@list} = # for every key listed in @list,
(1) x @list; # ...assign to it the corresponding value in this list
# which is (1, 1, 1, 1, 1...) (@list in scalar context
# gives the number of elements in the list)
Оператор x
описан по адресу perldoc perlop.
См. perldoc perldsc и perldoc perlreftut, где приведены учебные пособия по структурам данных и справочникам (как для начинающих, так и для экспертов). Сами фрагменты хэша упомянуты в perldoc perlreftut.
Что касается возврата хэша из функции, обычно следует возвращать сам хэш, а не ссылку. Ссылку можно использовать, если хэш имеет огромный размер , и это касается памяти или времени, но это не должно быть вашей первой заботой - заставить код работать.
Возвращаемые из функций значения всегда являются списками (где возвращаемый скаляр - это, по сути, список одного элемента). Хэши - это списки на Perl: Вы можете присваивать один другому взаимозаменяемо (предполагая, что список имеет четное количество элементов и нет коллизий ключей, которые привели бы к потере некоторых значений при преобразовании):
use strict; use warnings;
use Data::Dumper;
function foo
{
return qw(key1 value1 key2 value2);
}
my @list = foo();
my %hash = foo();
print Dumper(\@list);
print Dumper(\%hash);
дает:
$VAR1 = [
'key1',
'value1',
'key2',
'value2'
];
$VAR1 = {
'key2' => 'value2',
'key1' => 'value1'
};
PS. Я настоятельно рекомендую написать небольшие примеры программ, подобных вышеприведенным, чтобы поиграться со структурами данных и посмотреть, что произойдет. Вы можете многому научиться, экспериментируя!
Береги себя: a.pl
возвращает список с четным количеством элементов, а не хэш. Когда вы затем присваиваете такой список хэш-переменной, хэш будет построен с элементами в четных индексах в качестве ключей, а элементы в нечетных индексах в качестве значений. [EDIT: Я всегда так считал, но sub { ... %hash }
на самом деле ведет себя несколько иначе, чем sub { ... @list }
. ]
По той же самой причине, построение хэша, как вы описываете, так же просто, как и:
my %hash = map { $_ => 1 } qw(hi bi no th xc ul 8e r);
Мое личное правило - избегать ссылок, если только они мне действительно не нужны (например, вложенные структуры, или когда вам действительно нужно передать ссылку на ту же самую вещь).
EDIT: (Я больше не могу нажимать на ссылку "добавить комментарий"?! Используя клавиши мыши здесь...). Я немного подумал об этом и думаю, что передавать хэш-ссылки, наверное, в конце концов, лучше, благодаря тому, как мы используем хэш. Однако, вышеприведенный абзац все еще содержит ссылки на массив.
Спасибо за ваши комментарии Шверн и Эфир.