Как передать хэш функции в Perl?
У меня есть функция, которая принимает переменную и ассоциативный массив, но я не могу заставить их пройти правильно. Я думаю, что это как-то связано с объявлениями функций, однако я не могу понять, как они работают в Perl. Есть ли хорошая ссылка для этого и как я могу выполнить то, что мне нужно?
Я должен добавить, что он должен быть передан по ссылке.
sub PrintAA
{
my $test = shift;
my %aa = shift;
print $test . "\n";
foreach (keys %aa)
{
print $_ . " : " . $aa{$_} . "\n";
$aa{$_} = $aa{$_} . "+";
}
}
Ответы
Ответ 1
Передайте ссылку вместо самого хэша. Как и в
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}?
Ответ 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);
Ура!