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{$_} . "+";
    }
}
Foi útil?

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 sobre my(%hash) = ( 'aaa' => 1, 'bbb' => 'balls', 'ccc' => \&PrintAA ); contra my %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!

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top