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

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

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

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

9 ответов


передайте ссылку вместо самого хэша. Как в

PrintAA("abc", \%fooHash);

sub PrintAA
{
  my $test = shift;
  my $aaRef = shift;

  print $test, "\n";
  foreach (keys %{$aaRef})
  {
    print $_, " : ", $aaRef->{$_}, "\n";
  }
}

см. также perlfaq7:как я могу передать / вернуть {Function, FileHandle, Array, Hash, Method, Regex}?


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

#!/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() 'statement' в функции.


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

емко, Это заставляет его работать правильно.

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

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

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

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

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

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


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

разница в том, что { ... } нотация генерирует хэш-ссылку и ( ... ) нотация генерирует список, который сопоставляется с хэшем (в отличие от хэша 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.

кроме того:

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

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


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

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

PrintAA($foo, \%bar);

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

my %aa = shift;

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


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

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 потому что вы передаете его в качестве ссылки.


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

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

но так как у вас есть только два, вы могли бы уйти;) с передачей их прямо так:

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

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


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

чтобы создать хэш:

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

создать ссылку на этот хэш:

my $href = \%myhash

для доступа к этому хэшу по ссылке;

%$href

Итак, в вашем sub:

my $myhref = shift;

keys %$myhref;

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

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

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


используйте folowing 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);

Ура!