Home > Net >  Passing multiple Hashes as argument to Perl subroutine ( Perl 5.16 / Perl 5.30)
Passing multiple Hashes as argument to Perl subroutine ( Perl 5.16 / Perl 5.30)

Time:09-17

We are trying to pass multiple hashes together with bunch of scalars as arguments to a subroutine. The problem is in multiple calls of this subroutine, ( and if we print the two hashes after we retrieved them inside the function), only one of them ( the first) is getting proper value. The second one is coming off as null. We tried bunch of things -

1 ) two separate hashes with different syntaxes from Stackoverflow/PerlMonks etc with pass on as reference.

&mySub(\%hash0, \%hash1, $var0, $var1, $var2);

sub mySub{
   my (%hash0, %hash1, $var0, $var1, $var2) = (@_);
}

OR

&mySub(\%hash0, \%hash1, $var0, $var1, $var2);

sub mySub{
   my %hash0 = %{$_[0]};
   my %hash1 = %{$_[1]};
   my $var0  = $[2]; my $var1 = $[3]; my $var3 = $[4];
}

2 ) Create array of two hashes and pass

my @joined_arr = (\%hash0, \%hash1);
&mySub (\@joined_arr, $var0, $var1, $var2);

sub mySub{
   my (@joined_arr, $var0, $var1, $var2) = (@_);
   my %hash0 = %{joined_arr[0]};
   my %hash1 = %{joined_arr[1]};
   # rest of the variables.
}

4 ) Create hash of hashes and pass

my %joined_hash;
%joined_hash{"first_one"} = %hash0;
%joined_hash{"second_one"} = %hash1;
&mySub (\%joined_hash, $var0, $var1, $var2);

sub mySub{
   my %joined_hash, %hash0, %hash1;
   %joined_hash = %{$_[0]};
   %hash0 = %joined_hash{"first_one"};
   %hash1 = %joined_arr{"second_one"};
   # rest of the variables.
}
  1. Try them in Perl 5.16 ( default distribution of CentOS 7) and hen in Perl 5.30.

Till this point, this hasn't been a success. If anybody has an idea and like to share, it will be great help.


EDIT

Following the suggestion of @zdim and @Polar Bear, I have tried these things ->

Syntax for offloading @_ into scalars inside function ->

a) my ($ref_to_hash0, $ref_to_hash1, $var0, $var1, $var2) = @_;

b)

$ref_to_hash0 = shift;
$ref_to_hash1 = shift;
$var0 = shift;
$var1 = shift;    
$var2 = shift;

I have also tried these 3 style of hash-reference to hash assignment.

a) my %local_hash_shallow_copy = %$ref_to_hash0;

b) my $local_hashref_deep_copy = dclone $ref_to_hash0;

c) my %local_hash_shallow_copy = %{$ref_to_hash0};

It seems out of 9 iterations of this sub call, I am getting right hash inside the sub 2 times. At other times I simply get a pointer dumped -

$VAR1 = {
   'HASH(0x1e32cc8)' => undef
};

I am using Dumper to dump the hashes just Outside - right before the sub call, and just Inside - right after I transferred the value from ref to actual hash. This should avoid any silly mistake.

Either I might be doing a very basic mistake here or hit upon an uncanny issue. Am debugging it.

FYI.

CodePudding user response:

A function call in Perl passes a list of scalars as arguments, what you are correctly doing. The function receives a list of scalars in @_, which are aliases of those arguments.

So with the call

mySub(\%hash0, \%hash1, $var0, $var1, $var2);

the function gets in @_ five scalars, the first two being the hash-references of interest.

But now you assign those to a hash!

sub mySub{
   my (%hash0, %hash1, $var0, $var1, $var2) = (@_);   # WRONG
}

So that %hash0 is populated with everything in @_, as

%hash = (\%hash0 => \%hash1, $var0 => $var1, $var2 => undef);

since consecutive scalars are assigned as key-value pairs. The rest of the variables in the list, starting with %hash1, are also introduced as lexical symbols in that subroutine and are undef.

And you should be getting a warning if the number of arguments is indeed odd. (There is use warnings; line on the top of your program, right?)

Need to assign elements of @_ to suitable scalars, for example like

sub mySub{
   my ($ref_to_hash0, $ref_to_hash1, $var0, $var1, $var2) = @_;       
}

Now you have two options for how to work with this

  • Work directly with the reference(s), $ref_to_hash0. Like

    foreach my $key (keys %$ref_to_hash0) {
        $ref_to_hash0->{$key} ...
    }
    

    In short, this is

    • Efficient since you aren't copying data

    • It may be inefficient since every access needs to dereference

    • It allows you to change data in the caller by writing to $ref_to_hash0

      $ref_to_hash0->{some_key} = 'value';  # changes data IN THE CALLER
      

      This may be convenient, or dangerous (as it can be done by a mistake)

  • Make a local copy of the caller's data and work with that. Note that there may be a little catch with that, as you may need a deep copy

    use Storable qw(dclone);  # may be needed
    
    sub mySub{
        my ($ref_to_hash0, $refhash1, $var0, $var1, $var2) = @_;       
    
        # If the caller's %hash0 has no references for values just dereference
        # Tricky though -- what when the calling hash changes in the future?
        my %local_hash_shallow_copy = %$ref_to_hash0;
    
        # If the caller's %hash0 is a complex data structure, need a deep copy
        my $local_hashref_deep_copy = dclone $ref_to_hash0;
    
        # Changes to local hash/hashref do not affect data in the caller
    }
    

There is generally no need for that leading & in front of the name in the question, omitted here, unless you mean to suppress its prototype in the call.

CodePudding user response:

You can not to pass hash or an array into subroutine, instead you pass hash reference or array reference (\%hash or \@array).

In subroutine you should treat received argument as a reference. You can access data directly through reference $hashref->{key} or $arrayref->[index], or create a copy (more expensive on CPU cycles and memory) my %hash = %{$hashref} or my @array = @{$arrayref}.

Please see demonstration sample code bellow.

use strict;
use warnings;
use feature 'say';

use Data::Dumper;

my %hash_0 = ( data => [ 'it is snowing', 'this month', 'almost every day'] );
my %hash_1 = ( data => [ 1, 2, 5, 7, 11 ] );
my $var0 = 'The summer at it\'s end';
my $var1 = 3356;
my $var2 = 'Adorable child';

my $agregate = { hash_0 => \%hash_0, 
                 hash_1 => \%hash_1, 
                 var0   => $var0, 
                 var1   => $var1,
                 var2   => $var2 
            };
            
subHashes_1(\%hash_0, \%hash_1, $var0, $var1, $var2);
say "\n";
subHashes_2(\%hash_0, \%hash_1, $var0, $var1, $var2);
say "\n";
subHashes_3($agregate);
say "\n";
subHashes_4(\%hash_0, \%hash_1, $var0, $var1, $var2);

sub subHashes_1 {
    my($href_0, $href_1, $var0, $var1, $var2) = @_;
    
    say '--- subHashes_1 --------------';
    say 'Hash 0';
    say Dumper($href_0->{data});
    say 'Hash 1';
    say '-' x 45;
    say Dumper($href_1->{data});
    say 'Var0: ' . $var0;
    say 'Var1: ' . $var1;
    say 'Var2: ' . $var2;
}

sub subHashes_2 {
    my $href_0 = shift;
    my $href_1 = shift;
    my $var0   = shift;
    my $var1   = shift;
    my $var2   = shift;

    say '--- subHashes_2 --------------';
    say 'Hash 0';
    say Dumper($href_0->{data});
    say 'Hash 1';
    say '-' x 45;
    say Dumper($href_1->{data});
    say 'Var0: ' . $var0;
    say 'Var1: ' . $var1;
    say 'Var2: ' . $var2;
}

sub subHashes_3 {
    my $args = shift;
    
    my $href_0 = $args->{hash_0};
    my $href_1 = $args->{hash_1};
    my $var0   = $args->{var0};
    my $var1   = $args->{var1};
    my $var2   = $args->{var2};
    
    say '--- subHashes_3 --------------';
    say 'Hash 0';
    say Dumper($href_0->{data});
    say 'Hash 1';
    say '-' x 45;
    say Dumper($href_1->{data});
    say 'Var0: ' . $var0;
    say 'Var1: ' . $var1;
    say 'Var2: ' . $var2;
}

sub subHashes_4 {
    my $href_0 = shift;
    my $href_1 = shift;
    my $var0   = shift;
    my $var1   = shift;
    my $var2   = shift;

    say '--- subHashes_4 --------------';
    say 'Hash 0';
    say "\t$_ => " . join("\n\t",@{$href_0->{$_}}) for (keys %$href_0);
    say 'Hash 1';
    say '-' x 45;
    say "\t$_ => " . join("\n\t",@{$href_1->{$_}}) for (keys %$href_1);
    say 'Var0: ' . $var0;
    say 'Var1: ' . $var1;
    say 'Var2: ' . $var2;
}

Output

--- subHashes_1 --------------
Hash 0
$VAR1 = [
          'it is snowing',
          'this month',
          'almost every day'
        ];

Hash 1
---------------------------------------------
$VAR1 = [
          1,
          2,
          5,
          7,
          11
        ];

Var0: The summer at it's end
Var1: 3356
Var2: Adorable child


--- subHashes_2 --------------
Hash 0
$VAR1 = [
          'it is snowing',
          'this month',
          'almost every day'
        ];

Hash 1
---------------------------------------------
$VAR1 = [
          1,
          2,
          5,
          7,
          11
        ];

Var0: The summer at it's end
Var1: 3356
Var2: Adorable child


--- subHashes_3 --------------
Hash 0
$VAR1 = [
          'it is snowing',
          'this month',
          'almost every day'
        ];

Hash 1
---------------------------------------------
$VAR1 = [
          1,
          2,
          5,
          7,
          11
        ];

Var0: The summer at it's end
Var1: 3356
Var2: Adorable child


--- subHashes_4 --------------
Hash 0
        data => it is snowing
        this month
        almost every day
Hash 1
---------------------------------------------
        data => 1
        2
        5
        7
        11
Var0: The summer at it's end
Var1: 3356
Var2: Adorable child
  • Related