Как передать хэш функции в Perl?

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

Я должен добавить, что он должен быть передан по ссылке.

sub PrintAA
{
    my $test = shift;
    my %aa   = shift;
    print $test . "\n";
    foreach (keys %aa)
    {
        print $_ . " : " . $aa{$_} . "\n";
        $aa{$_} = $aa{$_} . "+";
    }
}

Ответ 2

Этот код работает:

#!/bin/perl -w

use strict;

sub PrintAA
{
    my($test, %aa) = @_;
    print $test . "\n";
    foreach (keys %aa)
    {
        print $_ . " : " . $aa{$_} . "\n";
    }
}

my(%hash) = ( 'aaa' => 1, 'bbb' => 'balls', 'ccc' => \&PrintAA );

PrintAA("test", %hash);

Ключевым моментом является использование контекста массива в операторе my() 'в функции.


Что делает бизнес контекста массива?

Вкратце, это заставляет его работать правильно.

Это означает, что первое значение в массиве аргументов @_ присваивается $test, а остальные элементы присваиваются хешу %aa. Учитывая способ, которым я его назвал, в @_ имеется нечетное количество элементов, поэтому, когда первый элемент назначен $test, существует четное количество элементов, доступных для назначения %aa, с первым элемент каждой пары является ключом ('aaa', 'bbb', 'ccc' в моем примере), а второй - соответствующее значение.

Можно было бы заменить %aa на @aa, и в этом случае в массиве было бы 6 элементов. Также было бы возможно заменить %aa на $aa, и в этом случае переменная $aa будет содержать значение "aaa", а остальные значения в @_ будут проигнорированы присвоением.

Если вы опускаете круглые скобки вокруг списка переменных, Perl отказывается компилировать код. Один из альтернативных ответов показал обозначение:

my $test = shift;
my(%aa) = @_;

Это в значительной степени эквивалентно тому, что я написал; разница заключается в том, что после двух операторов my @_ содержит только 6 элементов в этой вариации, тогда как в единственной версии my она все еще содержит 7 элементов.

В fooobar.com/questions/162032/... есть определенные вопросы о контексте массива.


Собственно, я не спрашивал о my($test, %aa) = @_;, о котором я спрашивал о my(%hash) = ( 'aaa' => 1, 'bbb' => 'balls', 'ccc' => \&PrintAA ); по сравнению с my %hash = { 'aaa' => 1, ... };

Отличие состоит в том, что нотация {...} генерирует хеш-ref, и нотация (...) генерирует список, который сопоставляется с хешем (в отличие от хеш-ref). Аналогично, [...] генерирует массив ref, а не массив.

Действительно, измените "основной" код, чтобы он читал: my (% hash) = {...}; и вы получаете ошибку времени выполнения (но не время компиляции) - обрабатывайте номера строк с осторожностью, так как я добавил альтернативные коды в свой файл:

Reference found where even-sized list expected at xx.pl line 18.
...
Use of uninitialized value in concatenation (.) or string at xx.pl line 13.

Ответ 3

В качестве альтернативы:

sub PrintAA
{
    my $test       = shift;
    my %aa         = @_;
        print $test . "\n";
        foreach (keys %aa)
        {
                print $_ . " : " . $aa{$_} . "\n";
                $aa{$_} = $aa{$_} . "+";
        }
}

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

Ответ 4

Похоже, вы должны передать ссылку на хэш.

sub PrintAA
{
   my $test = shift;
   my $aa = shift;
   if (ref($aa) != "HASH") { die "bad arg!" }
   ....
}

PrintAA($foo, \%bar);

Причина, по которой вы не можете сделать

my %aa = shift;

заключается в том, что Perl выравнивает все аргументы подпрограммы в один список, @_. Каждый элемент копируется, поэтому передача по ссылке также позволяет избежать этих копий.

Ответ 5

Как обычно, существует несколько способов. Вот что Perl Best Practices, который наиболее почитается указателями стиля, должен сказать о передаче параметров функциям:

Используйте хэш именованных аргументов для любой подпрограммы с более чем тремя параметрами

Но так как у вас всего двое, вы можете уйти;) с передачей их прямо следующим образом:

my $scalar = 5;
my %hash = (a => 1, b => 2, c => 3);

func($scalar, %hash)

И функция определяется следующим образом:

sub func {
    my $scalar_var = shift;
    my %hash_var = @_;

    ... Do something ...
}

Это может быть более полезно, если вы можете показать код.

Ответ 6

Все вышеописанные методы работают, но это всегда было так, как я предпочитал делать такие вещи:

sub PrintAA ($\%)
{
    my $test       = shift;
    my %aa         = ${shift()};
    print "$test\n";
    foreach (keys %aa)
    {
        print "$_ : $aa{$_}\n";
        $aa{$_} = "$aa{$_}+";
    }
}

Примечание. Я также немного изменил код. Строки с двойными кавычками Perl интерпретируют "$test" как значение $test, а не фактическую строку '$test', поэтому вам не нужно много . s.

Кроме того, я ошибался в отношении того, как работают прототипы. Чтобы передать хэш, используйте это:

PrintAA("test", %hash);

Чтобы напечатать хеш-ссылку, используйте следующую команду:

PrintAA("test", %$ref_to_hash);

Конечно, теперь вы не можете изменить хэш, на который ссылается $ref_to_hash, потому что вы отправляете копию, но вы можете изменить raw %hash, потому что вы передаете его как ссылку.

Ответ 7

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

Чтобы создать HASH:

my %myhash = ( key1 => "val1", key2 => "val2" );

Чтобы создать ссылку на этот HASH:

my $href = \%myhash

Чтобы получить доступ к этому хэшу по ссылке;

%$href

Итак, в вашем суб:

my $myhref = shift;

keys %$myhref;

Ответ 8

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

sub someFunction {
    my ( $arg1, $arg2, $arg3 ) = @_;

Это похоже на другие языки, где вы объявляете функции как

... someFunction ( arg1, arg2, arg3 )

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

sub testFunc {
    my ( $string, %hash ) = @_;
    print "$string $hash{'abc'} $hash{'efg'} $string\n";
}

my %testHash = (
    'abc' => "Hello",
    'efg' => "World"
);
testFunc('!!!', %testHash);

Вывод будет таким, как ожидалось:

!!! Hello World !!!

Это работает, потому что аргументы Perl всегда передаются как массив скалярных значений, и если вы передаете хеш, это ключевое значение/пары добавляются к этому массиву. В приведенном выше примере аргументы, переданные функции как array (@_), фактически:

'!!!', 'abc', 'Hello', 'efg', 'World'

и '!!!' просто присваивается %string, а %hash "проглатывает" все остальные аргументы, всегда интерпретируя один как ключ, а следующий - как значение (пока все элементы не будут исчерпаны).

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

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

Ответ 9

Используйте следующий sub для получения хэша или hashref - все, что передавалось:)

sub get_args { ref( $_[0] ) ? shift() : ( @_ % 2 ) ? {} : {@_}; }
sub PrintAA
{
  my $test = shift;
  my $aa = get_args(@_);;
  #then
  $aa->{somearg} #do something
  $aa->{anotherearg} #do something

}

Вызовите свою функцию следующим образом:

printAA($firstarg,somearg=>1, anotherarg=>2)

Или как это (неважно):

printAA($firstarg,{somearg=>1, anotherarg=>2})

Или даже так (неважно):

my(%hash) = ( 'aaa' => 1, 'bbb' => 'balls', 'ccc' => \PrintAA );

PrintAA("test", %hash);

Ура!