Зачем мне возвращать хеш или ссылку на хеш в Perl? - PullRequest
10 голосов
/ 06 января 2010

Какой самый эффективный способ выполнения ниже? (Я знаю, что они выполняют одно и то же, но как большинство людей делают это между тремя, и почему ?)

Файл a.pl

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;
}

Файл b.pl

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;
}

Файл c.pl

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;
}

Ответы [ 8 ]

22 голосов
/ 06 января 2010

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

my $value = build_hash()->{$key};

Научитесь любить хеш-ссылки, вы увидите их много, как только начнете использовать объекты.

9 голосов
/ 06 января 2010

Почему бы не вернуть оба? Контекст - очень мощная функция в 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
8 голосов
/ 06 января 2010

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

5 голосов
/ 06 января 2010

То, что вы ищете, это фрагмент хеша :

# 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 perldata .

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

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

2 голосов
/ 06 января 2010

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

Я собираюсь не согласиться с Эфиром здесь. Было время, когда я занял эту позицию, но быстро обнаружил, что погружаюсь в ад от необходимости запоминать, какие sub s возвращали хэши, а какие возвращали хэш-ссылки, что было довольно серьезным препятствием для того, чтобы заставить работать код. Важно стандартизировать либо всегда , возвращающий хэш / массив, либо всегда , возвращающий хеш-значение / arrayref, если только вы не хотите постоянно спотыкаться о себе.

Что касается стандартизации, я вижу несколько преимуществ использования ссылок:

  • Когда вы возвращаете хеш или массив, вы фактически возвращаете список, содержащий уплощенную копию исходного хеша / массива. Так же, как и передача параметров хеша / массива в sub, у него есть недостаток, заключающийся в том, что вы можете отправлять только один список за раз. Конечно, вам не нужно часто возвращать несколько списков значений, но это происходит, так почему же вы предпочитаете стандартизировать действия таким образом, чтобы это было невозможно?

  • Преимущества производительности / памяти (обычно незначительные) от возврата одного скаляра, а не потенциально гораздо большего куска данных.

  • Он поддерживает согласованность с ОО-кодом, который часто передает объекты (т. Е. Благословенные ссылки) назад и вперед.

  • Если по какой-либо причине важно, чтобы у вас была свежая копия хеша / массива, а не ссылка на оригинал, вызывающий код может легко создать его, как показано в OP в c.pl. Однако, если вы вернете копию хэша, вызывающая сторона не сможет превратить ее в ссылку на оригинал. (В тех случаях, когда это выгодно, функция может сделать копию и вернуть ссылку на копию, таким образом, защищая оригинал, избегая при этом " this возвращает хэши, , что возвращает хэш-ссылки" «черт, я упоминал ранее.)

  • Как упоминал Шверн, очень приятно иметь возможность сделать my $foo = $obj->some_data->{key}.

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

2 голосов
/ 06 января 2010

Я собираюсь пойти против фактора и того, что говорят все остальные, и сказать, что я предпочитаю, чтобы мои данные возвращались в виде хэша (ну, как список четного размера, который, вероятно, будет интерпретирован как хеш ). Я работаю в среде, где мы склонны делать такие вещи, как следующий фрагмент кода, и гораздо проще комбинировать, сортировать, разрезать и нарезать кубиками, когда вам не нужно разыменовывать каждую вторую строку. (Также приятно знать, что кто-то не может повредить ваш хэш-адрес, потому что вы передали всю вещь по значению. edit: , если у вас нет ссылок на другие объекты / хэши / массивы в хэш-значениях, тогда у вас все равно проблемы).

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.

2 голосов
/ 06 января 2010

a.pl и c.pl требуют, чтобы была взята копия хэша (и внутренний хэш функции помечен как свободная память). b.pl, с другой стороны, создает хэш всего один раз и требует немного дополнительной памяти для возврата ссылки, с которой вы можете работать. Таким образом, b.pl, скорее всего, будет наиболее эффективной формой из трех, как в пространстве, так и во времени.

1 голос
/ 06 января 2010

Берегите себя: a.pl возвращает список с четным числом элементов, а не с хэшем. Когда вы затем назначаете такой список хеш-переменной, хеш будет построен с элементами с четными индексами в качестве ключей и с элементами с нечетными индексами в качестве значений. [РЕДАКТИРОВАТЬ: Так я всегда видел, но sub { ... %hash } на самом деле ведет себя немного иначе, чем sub { ... @list }. ]

По той же причине создание хеша, как вы описываете, так же просто, как:

my %hash = map { $_ => 1 } qw(hi bi no th xc ul 8e r);

Мое личное правило - избегать ссылок, если они мне действительно не нужны (например, вложенные структуры или когда вам действительно необходимо передать ссылку на то же самое ).

РЕДАКТИРОВАТЬ: (Я больше не могу нажимать на ссылку «Добавить комментарий» ?! С помощью мышки здесь ...) Я немного подумал об этом и думаю, что, в конце концов, лучше обойти ссылки на хэш, потому что мы используем хэш. Тем не менее, параграф выше относится к ссылкам на массив.

Спасибо за ваши комментарии Шверн и Эфир.

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