Como passo um hash para uma função em Perl?
-
03-07-2019 - |
Pergunta
Eu tenho uma função que usa uma variável e um array associativo, mas não consigo fazê-los passar corretamente.Acho que isso tem algo a ver com declarações de funções, mas não consigo entender como elas funcionam em Perl.Existe uma boa referência para isso e como faço para realizar o que preciso?
Devo acrescentar que precisa ser passado por referência.
sub PrintAA
{
my $test = shift;
my %aa = shift;
print $test . "\n";
foreach (keys %aa)
{
print $_ . " : " . $aa{$_} . "\n";
$aa{$_} = $aa{$_} . "+";
}
}
Solução
Passe a referência em vez do próprio hash. Como em
PrintAA("abc", \%fooHash);
sub PrintAA
{
my $test = shift;
my $aaRef = shift;
print $test, "\n";
foreach (keys %{$aaRef})
{
print $_, " : ", $aaRef->{$_}, "\n";
}
}
Veja também Perlfaq7: Como posso passar/retornar um {function, fileHandle, matriz, hash, método, regex}?
Outras dicas
Este código funciona:
#!/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);
O ponto principal é o uso do contexto da matriz na 'declaração' my () na função.
O que os negócios de contexto da matriz realmente fazem?
Sucintamente, faz com que funcione corretamente.
Isso significa que o primeiro valor no @_
A variedade de argumentos é atribuída a $test
, e os itens restantes são atribuídos ao hash %aa
. Dada a maneira como eu chamei, há um número ímpar de itens no @_
, então quando o primeiro item for atribuído a $test
, há um número uniforme de itens disponíveis para atribuir %aa
, com o primeiro item de cada par sendo a chave ('aaa', 'bbb', 'ccc' no meu exemplo) e o segundo é o valor correspondente.
Seria possível substituir %aa
com @aa
, nesse caso, a matriz teria 6 itens. Também seria possível substituir %aa
com $aa
, e nesse caso, a variável $aa
conteria o valor 'aaa' e os valores restantes em @_
seria ignorado pela tarefa.
Se você omitir os parênteses em torno da lista de variáveis, o Perl se recusa a compilar o código. Uma das respostas alternativas mostrou a notação:
my $test = shift;
my(%aa) = @_;
Isso é praticamente equivalente ao que escrevi; A diferença é que depois dos dois my
declarações, @_
contém apenas 6 elementos nessa variação, enquanto no único my
Versão, ele ainda contém 7 elementos.
Definitivamente existem outras perguntas em ASSIM sobre o contexto da matriz.
Na verdade, eu não estava perguntando sobre o
my($test, %aa) = @_;
Eu estava perguntando sobremy(%hash) = ( 'aaa' => 1, 'bbb' => 'balls', 'ccc' => \&PrintAA );
contramy %hash = { 'aaa' => 1, ... };
A diferença é que a notação {...} gera um hash ref e a notação (...) gera uma lista, que mapeia um hash (em oposição a hash ref). Da mesma forma, [...] gera uma referência de matriz e não uma matriz.
De fato, altere o código 'principal' para que ele lê: meu (%hash) = {...}; E você obtém um erro de tempo de execução (mas não de compilação) - trate os números de linha com cautela, pois adicionei codificações alternativas ao meu arquivo:
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.
Alternativamente:
sub PrintAA
{
my $test = shift;
my %aa = @_;
print $test . "\n";
foreach (keys %aa)
{
print $_ . " : " . $aa{$_} . "\n";
$aa{$_} = $aa{$_} . "+";
}
}
O que você está perdendo fundamentalmente é que uma matriz associativa não é um único argumento (embora uma referência de matriz associativa seja, como na resposta de Paul Tomblin).
Parece que você deve passar em uma referência a um hash.
sub PrintAA
{
my $test = shift;
my $aa = shift;
if (ref($aa) != "HASH") { die "bad arg!" }
....
}
PrintAA($foo, \%bar);
A razão pela qual você não pode fazer um
my %aa = shift;
é porque o Perl achata todos os argumentos para uma sub -rotina em uma lista, @_. Todo elemento é copiado, portanto, passar por referência evita essas cópias também.
Como sempre, existem várias maneiras. Aqui está o quê Perl práticas recomendadas, que mais reverenciado de indicadores de estilo tem a dizer sobre os parâmetros de aprovação para as funções:
Use um hash de argumentos nomeados para qualquer sub -rotina que tenha mais de três parâmetros
Mas como você tem apenas dois, você pode fugir;) passando por eles diretamente assim:
my $scalar = 5;
my %hash = (a => 1, b => 2, c => 3);
func($scalar, %hash)
E a função é definida assim:
sub func {
my $scalar_var = shift;
my %hash_var = @_;
... Do something ...
}
Pode ser mais útil se você pudesse mostrar algum código.
Todos os métodos acima funcionam, mas sempre foi assim que eu preferia fazer coisas assim:
sub PrintAA ($\%)
{
my $test = shift;
my %aa = ${shift()};
print "$test\n";
foreach (keys %aa)
{
print "$_ : $aa{$_}\n";
$aa{$_} = "$aa{$_}+";
}
}
Nota: Eu também mudei um pouco seu código. As seqüências de dupla ponta de Perl interpretarão "$test"
para ser o valor de $test
ao invés da sequência real '$test'
, então você não precisa de tantos .
s.
Além disso, eu estava errado sobre como os protótipos funcionam. Para passar um hash, use isto:
PrintAA("test", %hash);
Para imprimir uma referência de hash, use isso:
PrintAA("test", %$ref_to_hash);
Claro, agora você não pode modificar o hash referenciado por $ref_to_hash
Porque você está enviando uma cópia, mas você pode modificar um bruto %hash
Porque você está passando como uma referência.
Argumentos para as funções são achatados em uma única matriz (@_). Portanto, geralmente é mais fácil passar hashes para funcionar por referência.
Para criar um hash:
my %myhash = ( key1 => "val1", key2 => "val2" );
Para criar uma referência a esse hash:
my $href = \%myhash
Acessar esse hash por referência;
%$href
Então, em seu sub:
my $myhref = shift;
keys %$myhref;
Todas as outras respostas aqui até agora parecem bastante complicadas para mim.Quando escrevo uma função Perl, normalmente "expanda" todos os argumentos passados na primeira linha da função.
sub someFunction {
my ( $arg1, $arg2, $arg3 ) = @_;
Isso é semelhante a outras linguagens, onde você declara funções como
... someFunction ( arg1, arg2, arg3 )
E se você fizer dessa forma e passar o hash como último argumento, você ficará bem sem nenhum truque ou magia especial.Por exemplo.:
sub testFunc {
my ( $string, %hash ) = @_;
print "$string $hash{'abc'} $hash{'efg'} $string\n";
}
my %testHash = (
'abc' => "Hello",
'efg' => "World"
);
testFunc('!!!', %testHash);
A saída é a esperada:
!!! Hello World !!!
Isso funciona porque em Perl os argumentos são sempre passados como uma matriz de valores escalares e se você passar um hash, seus valores/pares de chave serão adicionados a essa matriz.No exemplo acima, os argumentos passados para a função como array (@_
) são na verdade:
'!!!', 'abc', 'Hello', 'efg', 'World'
e '!!' é simples atribuído a %string
, enquanto %hash
"engole" todos os outros argumentos, sempre interpretando um como chave e o próximo como valor (até que todos os elementos se esgotem).
Você não pode passar vários hashes dessa maneira e o hash não pode ser o primeiro argumento, caso contrário ele engoliria tudo e deixaria todos os outros argumentos sem atribuir.
É claro que exatamente o mesmo funciona para array como último argumento.A única diferença aqui é que os arrays não distinguem entre chaves e valores, para eles todos os argumentos que sobraram são valores e apenas são enviados para o array.
Use o Sub Following para obter hash ou hashref - o que for passado :)
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
}
Ligue para sua função assim:
printAA($firstarg,somearg=>1, anotherarg=>2)
Ou assim (não importa):
printAA($firstarg,{somearg=>1, anotherarg=>2})
Ou mesmo assim (não importa):
my(%hash) = ( 'aaa' => 1, 'bbb' => 'balls', 'ccc' => \PrintAA );
PrintAA("test", %hash);
Felicidades!