web-dev-qa-db-fra.com

Comment passer d'un hachage à une fonction dans Perl?

J'ai une fonction qui prend une variable et un tableau associatif, mais je n'arrive pas à les faire passer correctement. Je pense que cela a quelque chose à voir avec les déclarations de fonction, mais je ne comprends pas comment elles fonctionnent en Perl. Y at-il une bonne référence pour cela et comment puis-je accomplir ce dont j'ai besoin?

Je devrais ajouter qu'il doit être transmis par référence.

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

Passez la référence au lieu du hachage lui-même. Un péché

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 transmettre/retourner une {Fonction, FileHandle, Array, Hash, Méthode, Regex}?

65
Paul Tomblin

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 de tableau dans l'instruction my () de la fonction.


Que fait réellement l’entreprise de contexte de tableau?

En résumé, cela fonctionne correctement.

Cela signifie que la première valeur du tableau d'arguments @_ est affectée à $test et que les éléments restants sont affectés à la table de hachage %aa. De la façon dont je l'ai appelé, il y a un nombre impair d'éléments dans le @_. Ainsi, une fois que le premier élément est attribué à $test, il existe un nombre pair d'éléments disponibles à affecter à %aa, le premier élément de chaque paire étant le key ('aaa', 'bbb', 'ccc' dans mon exemple), le second étant la valeur correspondante.

Il serait possible de remplacer %aa par @aa, auquel cas le tableau aurait 6 éléments. Il serait également possible de remplacer %aa par $aa et, dans ce cas, la variable $aa contiendrait la valeur 'aaa' et les valeurs restantes dans @_ seraient ignorées par l'affectation.

Si vous omettez les parenthèses autour de la liste des variables, Perl refuse de compiler le code . Une des réponses possibles indiquait la notation:

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

C'est à peu près équivalent à ce que j'ai écrit; la différence est qu'après les deux instructions my, @_ ne contient que 6 éléments dans cette variante, alors que dans la version unique my, il contient toujours 7 éléments.

Il y a certainement d'autres questions dans SO à propos du contexte de tableau.


En fait, je ne posais pas de question sur le my($test, %aa) = @_; Je parlais de my(%hash) = ( 'aaa' => 1, 'bbb' => 'balls', 'ccc' => \&PrintAA ); par rapport à my %hash = { 'aaa' => 1, ... };

La différence est que la notation {...} génère un hacha ref et que la notation (...) génère une liste, qui mappe sur un hachage (par opposition à un hacha ref). De même, [...] génère un tableau ref et non un tableau.

En effet, changez le code 'principal' pour qu'il se lise: my (% hash) = {...}; et vous obtenez une erreur d'exécution (mais pas de compilation) - traitez les numéros de ligne avec prudence car j'ai ajouté des codages 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
Jonathan Leffler

Alternativement:

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

Ce qui vous manque fondamentalement, c'est qu'un tableau associatif n'est pas un argument unique (bien qu'une référence à un tableau associatif le soit, comme dans la réponse de Paul Tomblin).

12
chaos

Il semblerait que vous deviez passer 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;

en effet, Perl aplatit tous les arguments d'une sous-routine en une seule liste, @_. Chaque élément est copié, le fait de passer par référence évite également ces copies.

4
mkb

Comme d'habitude, il y a plusieurs façons. Voici ce que Meilleures pratiques Perl, le plus respecté des pointeurs de style, a à propos du passage de paramètres à des fonctions:

Utilise un hachage d'arguments nommés pour toute sous-routine ayant plus de trois paramètres

Mais puisque tu n’en as que deux, tu peux t'enfuir;) 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 ...
}

Cela pourrait être plus utile si vous pouviez montrer du code.

4
Ya. Perelman

Toutes les méthodes ci-dessus fonctionnent, mais c'est toujours ainsi que j'ai préféré faire les choses suivantes:

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

Note: j'ai aussi un peu changé votre code. Les chaînes entre guillemets doubles de Perl interpréteront "$test" comme étant la valeur de $test plutôt que la chaîne réelle '$test', de sorte que vous n'aurez pas besoin de tant de .s.

De plus, je me suis trompé sur le fonctionnement des prototypes. Pour passer un hachage, utilisez ceci:

PrintAA("test", %hash);

Pour imprimer une référence de hachage, utilisez ceci:

PrintAA("test", %$ref_to_hash);

Bien sûr, vous ne pouvez plus modifier le hachage référencé par $ref_to_hash car vous envoyez une copie, mais vous pouvez modifier un %hash brut car vous le transmettez en tant que référence.

3
Chris Lutz

Toutes les autres réponses ici à ce jour me semblent plutôt compliquées. Lorsque j'écris une 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 ) = @_;

Ceci est similaire aux autres langues, où vous déclarez des fonctions en tant que

... someFunction ( arg1, arg2, arg3 )

Et si vous le faites de cette façon et que vous passez le hachage comme dernier argument, vous pourrez vous en passer, sans astuce ni magie particulière. 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 car en Perl, les arguments sont toujours passés en tant que tableau de valeurs scalaires et si vous passez un hachage, sa valeur clé/paires sont ajoutées à ce tableau. Dans l'exemple ci-dessus, les arguments passés à la fonction en tant que tableau (@_) sont en fait:

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

et '!!!' est simplement assigné à %string, tandis que %hash "engloutit" tous les autres arguments, en interprétant toujours un comme une clé et le suivant comme une valeur (jusqu'à ce que tous les éléments soient épuisés).

Vous ne pouvez pas transmettre plusieurs hachages de cette façon et le hachage ne peut pas être le premier argument, sinon il avalerait tout et laisserait tous les autres arguments non affectés. 

Bien sûr, la même chose fonctionne pour array comme dernier argument. La seule différence est que les tableaux ne font pas la distinction entre les clés et les valeurs, pour eux tous les arguments restants sont des valeurs et sont simplement poussés vers le tableau.

1
Mecki

Les arguments des fonctions sont aplatis dans un seul tableau (@_). Il est donc généralement plus facile de passer des hachages à 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

Donc dans votre sous-marin:

my $myhref = shift;

keys %$myhref;
1
Arnshea

Utilisez le sous-menu suivant pour obtenir hash ou hashref - tout ce qui a été 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 ça (peu importe):

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

Ou même comme ça (peu importe):

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

PrintAA("test", %hash);

À votre santé!

0
Беров