1

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.

SS891
  • 39
  • 4
  • Can you provide a minimal example demonstrating the problem? Then we can try to reproduce. See [mcve] for more information – Håkon Hægland Aug 23 '21 at 19:16
  • Hi @HåkonHægland, I can try that. I am gonna try/tinker with solutions here and then put some code here. – SS891 Aug 23 '21 at 20:10

2 Answers2

8

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 variable in the subroutine gets populated with everything in @_, like

# MySub variable      # variables from the caller, (their aliases) passed in
my %hash0         =   (\%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?)

Instead, 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.

zdim
  • 64,580
  • 5
  • 52
  • 81
  • Hi zdim, thanks a bunch for explaining the details. Would try them out now. Also there's "use strict;" and "use warning;" in the code. – SS891 Aug 23 '21 at 19:40
  • @Ad891 Note the last edit (you may need a "deep copy" in the sub), and please also come back from time to time to check for more changes, since I am not sure how to concisely enough enlist all that matters for such a basic topic, so there'll probably be edits :). Good that you have `warnings` and `strict` --- _always_ – zdim Aug 23 '21 at 19:47
  • Hi @zdim, yes I have followed deep-copy but outside while passing the hash reference. The idea the original hash shouldn't be touched. Also now I recall, I have tried a close to your last idea of hash reference, basically - a) same hash references and rest of scalar offloading from @_; b) declare local hashesh "my( %local_hash0, %local_hash1);" c) assign the pointers "$local_hash0 = $ref_to_hash0; $local_hash1 = $ref_to_hash1;" Some this approach and the solution you have discussed, leads to correct hash assignment in 2 calls of this sub. In 2 other cases, that doesn't happen. – SS891 Aug 23 '21 at 19:57
  • I am trying to debug further including using another dclone inside as well to insulate it better. Thank you for the details!. – SS891 Aug 23 '21 at 19:57
  • I have added some edit to original questions. Tried method suggested by you both. Something basic might have been missed or I am going into an unknown zone as the scheme seems to work in 2 out of 9 iterations of the code. – SS891 Aug 24 '21 at 07:13
  • @Ad891 Everything in the "**EDIT**" seems correct? I'll look more carefully tomorrow (it's getting late) but it looks like it's something else. Here's a simple program to enter (copy-paste) on the command-line ("one-liner"): `perl -Mstrict -wE'sub ff { my ($hr) = @_; my %local_hash = %$hr; say "$_ => $local_hash{$_}" for keys %local_hash }; my %hash = (a=>1, b=>2); ff(\%hash)'` This creates a hash and passes it to a function, in which it is dereferenced into a local-to-function hash (since you need that it seems) and printed. – zdim Aug 24 '21 at 07:34
  • Thanks @zdim. I will try this. Digging up more. – SS891 Aug 24 '21 at 08:32
2

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
Polar Bear
  • 6,762
  • 1
  • 5
  • 12
  • Thanks for all the details!... The only problem here is that contents of each hash isn't known. The formation of hashes and access of the subroutine happen in loop and we can't sure about first level keys to access it. "my %hash = %{$hashref} " This might be optimal way for us to copy it over or deepclone zdim suggested in answer 1. – SS891 Aug 23 '21 at 20:08
  • I have added some edit to original questions. Tried method suggested by you both. Something basic might have been missed or I am going into an unknown zone as the scheme seems to work in 2 out of 9 iterations of the code. – SS891 Aug 24 '21 at 07:13