Comment passer un hachage à une fonction en Perl?

j'ai une fonction qui prend une variable et un tableau associatif, mais je n'arrive pas à les faire passer à droite. Je pense que cela a quelque chose à voir avec les déclarations de fonction, mais je ne peux pas comprendre comment ils fonctionnent en Perl. Est-il une bonne référence pour le présent et comment dois-je accomplir ce dont j'ai besoin?

je dois ajouter qu'il doit être passé par référence.

sub PrintAA
{
    my $test = shift;
    my %aa   = shift;
    print $test . "n";
    foreach (keys %aa)
    {
        print $_ . " : " . $aa{$_} . "n";
        $aa{$_} = $aa{$_} . "+";
    }
}
34
demandé sur Peter Mortensen 2009-04-29 23:08:34

9 réponses

passer la référence au lieu du hachage lui-même. Comme dans

PrintAA("abc", \%fooHash);

sub PrintAA
{
  my $test = shift;
  my $aaRef = shift;

  print $test, "\n";
  foreach (keys %{$aaRef})
  {
    print $_, " : ", $aaRef->{$_}, "\n";
  }
}

Voir aussi perlfaq7: Comment puis-je passer/retour a {Function, Descripteur de fichier, de Tableau, de Hachage, la Méthode, la Regex}?

64
répondu Paul Tomblin 2009-04-29 20:45:23

ce code fonctionne:

#!/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);

le point clé est l'utilisation du contexte du tableau dans l'instruction my() 'statement' dans la fonction.


Que fait réellement l'activité array context?

succinctement, il fait fonctionner correctement.

signifie que la première valeur dans le tableau @_ d'arguments est assignée à $test , et les autres articles sont assignés au hachage %aa . Étant donné la façon dont je l'ai appelé, il y a un nombre impair d'articles dans le @_ , donc une fois que le premier article est assigné à $test , il y a un nombre pair d'articles disponibles à attribuer à %aa , le premier article de chaque paire étant la clé ("aaa", "bbb", " ccc " dans mon exemple), et le second étant la valeur correspondante.

il serait possible de remplacer %aa par @aa , dans ce cas, la matrice de 6 éléments. Il serait également possible de remplacer %aa avec $aa , et dans ce cas, la variable $aa contient la valeur "aaa", et les valeurs restantes dans @_ sera ignoré par la cession.

si vous omettez les parenthèses autour de la liste des variables, Perl refuse de compiler le code. Une des réponses alternatives a montré la notation:

my $test = shift;
my(%aa) = @_;

This est à peu près équivalent à ce que j'ai écrit; la différence est qu'après les deux my déclarations, @_ ne contient que 6 éléments dans cette variation, alors que dans la seule my version, il contient encore 7 éléments.

il y a certainement d'autres questions dans SO à propos du contexte array.


en fait, je ne demandais pas à propos du my($test, %aa) = @_; j'étais à propos de my(%hash) = ( 'aaa' => 1, 'bbb' => 'balls', 'ccc' => \&PrintAA ); versus my %hash = { 'aaa' => 1, ... };

La différence est que le { ... } la notation génère un hash ref et le ( ... ) la notation génère une liste, qui correspond à un hash (par opposition à un ref hash). Pareillement. [ ,.. ] génère un tableau arbitre et non un tableau.

en effet, changez le code 'main' pour qu'il se lise: my(%hash) = { ... }; et vous obtenez un moment de l'exécution (mais pas le temps de compilation) erreur - traiter les numéros de ligne avec prudence, puisque les J'ai ajouté des codes alternatifs à mon fichier:

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.
16
répondu Jonathan Leffler 2017-05-23 12:16:51

alternativement:

sub PrintAA
{
    my $test       = shift;
    my %aa         = @_;
        print $test . "\n";
        foreach (keys %aa)
        {
                print $_ . " : " . $aa{$_} . "\n";
                $aa{$_} = $aa{$_} . "+";
        }
}

la chose que vous manquez fondamentalement est qu'un tableau associatif n'est pas un argument simple (bien qu'une référence au tableau associatif soit, comme dans la réponse de Paul Tomblin).

12
répondu chaos 2009-08-04 23:28:18

il semble que vous devriez passer dans une référence à un hachage.

sub PrintAA
{
   my $test = shift;
   my $aa = shift;
   if (ref($aa) != "HASH") { die "bad arg!" }
   ....
}

PrintAA($foo, \%bar);

La raison pour laquelle vous ne pouvez pas faire un

my %aa = shift;

est parce que Perl aplatit tous les arguments à un sous-programme dans une liste, @_. Chaque élément est copié, donc passer par référence évite ces copies aussi.

4
répondu mkb 2016-08-29 13:03:33

toutes les méthodes ci-dessus fonctionnent, mais c'était toujours la façon dont je préférais faire les choses comme ceci:

sub PrintAA ($\%)
{
    my $test       = shift;
    my %aa         = ${shift()};
    print "$test\n";
    foreach (keys %aa)
    {
        print "$_ : $aa{$_}\n";
        $aa{$_} = "$aa{$_}+";
    }
}

Note: j'ai aussi changé un peu votre code. Les chaînes à double citation de Perl interpréteront "$test" comme la valeur de $test plutôt que la chaîne réelle '$test' , donc vous n'avez pas besoin de beaucoup de . S.

aussi, je me suis trompé sur la façon dont les prototypes fonctionnent. Pour passer un hachage, utilisez ceci:

PrintAA("test", %hash);

pour imprimer une référence hash, utilisez ceci:

PrintAA("test", %$ref_to_hash);

bien sûr, maintenant vous ne pouvez pas modifier le hachage référencé par $ref_to_hash parce que vous envoyez une copie, mais vous pouvez modifier un raw %hash parce que vous le Passez comme référence.

3
répondu Chris Lutz 2009-04-29 20:51:55

Comme d'habitude, il y a plusieurs façons. Voici ce que "meilleures pratiques Perl , qui révère le plus des pointeurs de style, A à dire sur le passage des paramètres aux fonctions:

utilisez un hachage d'arguments nommés pour tout sous-programme qui a plus de trois paramètres

mais comme vous n'en avez que deux, vous pouvez vous en tirer ;) en les passant directement comme ceci:

my $scalar = 5;
my %hash = (a => 1, b => 2, c => 3);

func($scalar, %hash)

et la fonction est définie comme ceci:

sub func {
    my $scalar_var = shift;
    my %hash_var = @_;

    ... Do something ...
}

il pourrait être plus utile si vous pouviez montrer un certain code.

3
répondu Ya. Perelman 2016-08-29 13:06:47

les Arguments pour les fonctions s'aplatissent en un seul tableau (@_). Il est donc généralement plus facile de passer des hashes pour fonctionner par référence.

pour créer un hachage:

my %myhash = ( key1 => "val1", key2 => "val2" );

pour créer une référence à ce hachage:

my $href = \%myhash

pour accéder à ce hachage par référence;

%$href

dans votre sujet:

my $myhref = shift;

keys %$myhref;
1
répondu Arnshea C 2009-04-29 19:51:04

toutes les autres réponses reçues jusqu'ici me semblent assez compliquées. Quand J'écris la fonction Perl, je "développe" habituellement tous les arguments passés dans la première ligne de la fonction.

sub someFunction {
    my ( $arg1, $arg2, $arg3 ) = @_;

c'est similaire à d'autres langues, où vous déclarez des fonctions comme

... someFunction ( arg1, arg2, arg3 )

et si vous le faites de cette façon et passez le hachage comme dernier argument, vous serez très bien sans aucune astuce ou magie spéciale. Par exemple:

sub testFunc {
    my ( $string, %hash ) = @_;
    print "$string $hash{'abc'} $hash{'efg'} $string\n";
}

my %testHash = (
    'abc' => "Hello",
    'efg' => "World"
);
testFunc('!!!', %testHash);

La sortie est comme prévu:

!!! Hello World !!!

cela fonctionne parce que dans les arguments Perl sont toujours passés comme un tableau de valeurs scalaires et si vous passez un hachage, c'est la valeur clé/les paires sont ajoutées à ce tableau. Dans l'exemple ci-dessus, les arguments passés à la fonction array ( @_ ) sont en fait:

'!!!', 'abc', 'Hello', 'efg', 'World'

et"!!!"est simplement assigné à %string , alors que %hash "avale" tous les autres arguments, interprétant toujours un comme clé et la suivante comme valeur (jusqu'à ce que tous les éléments soient épuisés).

vous ne pouvez pas passer plusieurs hashs de cette façon et le hashs ne peut pas être le premier argument, car sinon il avalerait tout et laisserait tous les autres arguments non assignés.

bien sûr exactement les mêmes travaux pour le tableau qu'un dernier argument. La seule différence ici est que les tableaux ne distinguent pas entre les clés et les valeurs, pour eux tous les arguments restants sont des valeurs et juste obtenir poussé à la baie.

1
répondu Mecki 2015-02-11 15:25:05

utilisez le sous-marin suivant pour obtenir hash ou hashref - peu importe ce qui est passé:)

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

}

appelez votre fonction comme ceci:

printAA($firstarg,somearg=>1, anotherarg=>2)

Ou comme ceci(n'importe):

printAA($firstarg,{somearg=>1, anotherarg=>2})

Ou encore comme ce(n'importe):

my(%hash) = ( 'aaa' => 1, 'bbb' => 'balls', 'ccc' => \PrintAA );

PrintAA("test", %hash);

santé!

0
répondu Беров 2009-04-29 19:49:29