Как передать хэш функции в Perl?
-
03-07-2019 - |
Вопрос
У меня есть функция, которая принимает переменную и ассоциативный массив, но я не могу заставить их пройти правильно.Я думаю, что это как-то связано с объявлениями функций, однако я не могу понять, как они работают в Perl.Есть ли хорошая ссылка на это и как мне сделать то, что мне нужно?
Добавлю, что его нужно передавать по ссылке.
sub PrintAA
{
my $test = shift;
my %aa = shift;
print $test . "\n";
foreach (keys %aa)
{
print $_ . " : " . $aa{$_} . "\n";
$aa{$_} = $aa{$_} . "+";
}
}
Решение
Передайте ссылку вместо самого хеша.Как в
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() в функции.
Что на самом деле делает бизнес с контекстом массива?
Короче говоря, это заставляет его работать правильно.
Это означает, что первое значение в @_
массив аргументов присваивается $test
, а остальные элементы присваиваются хешу %aa
.Учитывая то, как я это назвал, в файле нечетное количество элементов. @_
, поэтому, как только первый элемент будет назначен $test
, существует четное количество элементов, которым можно назначить %aa
, где первый элемент каждой пары является ключом (в моем примере «aaa», «bbb», «ccc»), а второй — соответствующим значением.
Можно было бы заменить %aa
с @aa
, и в этом случае массив будет содержать 6 элементов.Также можно было бы заменить %aa
с $aa
, и в этом случае переменная $aa
будет содержать значение «ааа», а остальные значения в @_
будет игнорироваться назначением.
Если вы опустите круглые скобки вокруг списка переменных, 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).Сходным образом, [ ...] генерирует ссылку на массив, а не массив.
Действительно, измените «основной» код так, чтобы он гласил:мой(%хэш) = { ...};и вы получите ошибку во время выполнения (но не во время компиляции) - относитесь к номерам строк с осторожностью, поскольку я добавил в свой файл альтернативные кодировки:
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 объединяет все аргументы подпрограммы в один список @_.Копируется каждый элемент, поэтому передача по ссылке также позволяет избежать этих копий.
Как обычно, есть несколько способов.Вот что Лучшие практики 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 ...
}
Было бы полезнее, если бы вы показали какой-нибудь код.
Все вышеперечисленные методы работают, но я всегда предпочитал делать такие вещи:
sub PrintAA ($\%)
{
my $test = shift;
my %aa = ${shift()};
print "$test\n";
foreach (keys %aa)
{
print "$_ : $aa{$_}\n";
$aa{$_} = "$aa{$_}+";
}
}
Примечание:Я также немного изменил ваш код.Строки Perl в двойных кавычках будут интерпретироваться "$test"
быть ценностью $test
а не фактическая строка '$test'
, так что вам не нужно так много .
с.
Кроме того, я ошибался насчет того, как работают прототипы.Чтобы передать хеш, используйте это:
PrintAA("test", %hash);
Чтобы распечатать ссылку на хеш, используйте это:
PrintAA("test", %$ref_to_hash);
Конечно, теперь вы не можете изменить хэш, на который ссылается $ref_to_hash
потому что вы отправляете копию, но можете изменить необработанный файл %hash
потому что вы передаете его как ссылку.
Аргументы функций сводятся в один массив (@_).Поэтому обычно проще всего передавать хеши в функцию по ссылке.
Чтобы создать ХЕШ:
my %myhash = ( key1 => "val1", key2 => "val2" );
Чтобы создать ссылку на этот HASH:
my $href = \%myhash
Чтобы получить доступ к этому хешу по ссылке;
%$href
Итак, в вашем сабвуфере:
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 аргументы всегда передаются как массив скалярных значений, и если вы передаете хеш, его ключевое значение/пары добавляются к этому массиву.В приведенном выше примере аргументы передаются функции в виде массива (@_
) на самом деле:
'!!!', 'abc', 'Hello', 'efg', 'World'
и '!!!' просто назначен %string
, пока %hash
«проглатывает» все остальные аргументы, всегда интерпретируя один как ключ, а следующий как значение (пока все элементы не будут израсходованы).
Вы не можете передать таким образом несколько хэшей, и хеш не может быть первым аргументом, так как в противном случае он поглотит все и оставит все остальные аргументы неназначенными.
Конечно, то же самое работает и с массивом в качестве последнего аргумента.Единственная разница здесь в том, что массивы не различают ключи и значения, для них все оставшиеся аргументы являются значениями и просто помещаются в массив.
Используйте следующую подпрограмму, чтобы получить хэш или хэш-ссылку — что бы ни прошло :)
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);
Ваше здоровье!