Как передать хэш функции в 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);
Ура!