greg
greg

Reputation: 23

passing a hash to a subroutine(s)

I'm working on a project for myself and I've been having an issue with passing a hash to a subroutine. There's several subroutines actually but I tried to strip it down to the bare minimum. I realize that the %mdc is a global hash so I don't have to pass it around to all of the subroutines I have but I'd prefer it that way out of habit. While the below snippet does work it feels like it could be cleaner.

Anyway, the part that's got my thinking that there must be a better way are the lines:

%mdc_def = mysub(\%mdc);
%mdc = %$mdc_def;

It feels... klunky to me but I'm not finding a way to make those two lines be just one... something like (even though it's bad syntax):

%mdc = %mysub(\%mdc);

Then if I should have a mysub2 that would also need that hash I'd have to do the same two line shuffle in that subroutine as well. It just feels messy.

Here's the snippet that I'm working on:

use strict;
use warnings;

my %mdc = ();
my $mdc_def;

$mdc{abc} = 123;
$mdc{cde} = 234;

foreach (sort keys (%mdc)) { print "before $_ = $mdc{$_}\n"; }
$mdc_def = mysub(\%mdc);
%mdc = %$mdc_def;
foreach (sort keys (%mdc)) { print " after $_ = $mdc{$_}\n"; }

sub mysub {
  my ($mdc_def) = @_;
  my %m = %$mdc_def;

  $m{def} = 345;
  $m{efg} = 456; 

  return \%m;
}

Thanks in advance.

Upvotes: 2

Views: 136

Answers (3)

ikegami
ikegami

Reputation: 385590

You could use the following:

sub mysub {
   my ($mdc_def) = @_;
   my %m = %$mdc_def;

   $m{def} = 345;
   $m{efg} = 456; 

   return \%m;
}

my %mdc = ( a=>1, b=>2 );

%mdc = %{ mysub(\%mdc) };

But that makes not one by two copies of hashes and all the scalars within. There's no reason for the global var to be a hash instead of scalar, which would avoid one of the instances of copying.

sub mysub {
   my ($mdc_def) = @_;
   my %m = %$mdc_def;

   $m{def} = 345;
   $m{efg} = 456; 

   return \%m;
}

my $mdc = { a=>1, b=>2 };

$mdc = mysub(\%mdc);

That leaves one instance of copying. That instance is needed if you don't want mysub to modify the hash in-place, so the above code is fine.

The following a version that modidies the hash in-place, avoiding the second instance of copying (and allowing us to leave the global var as a hash).

sub mysub_inplace {
   my ($mdc_def) = @_;
   $mdc_def->{def} = 345;
   $mdc_def->{efg} = 456; 
}

my %mdc = ( a=>1, b=>2 );

mysub_inplace(\%mdc);

Upvotes: 5

AnFi
AnFi

Reputation: 10903

Try

%mdc = %{mysub(\%mdc)};
  • \%mdc will pass reference to %mdc hash as parameter to mysub suroutine
  • %{ mysub(…)} will treat return value of mysub call as hash reference and convert it to hash

You may use subroutine prototype sub mysub (\%) to call mysub with mysub(%mdc)


Simple working example/script:

#!/usr/bin/perl
# Original version constained prototypes for sub mysub - 
# see ikegami comments below the answear
use strict;
use warnings;

sub mysub {
  my( $hRef ) = (@_);
  $hRef->{a} = 0;
  $hRef->{c} = 3;
  return $hRef;
}

my %mdc = ( a=>1, b=>2);

%mdc = %{mysub(\%mdc)};
print %mdc;

Upvotes: 1

Polar Bear
Polar Bear

Reputation: 6798

Do you possess an understanding what is $hash_ref = \%hash and how to use it?

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

my %mdc = ();

$mdc{abc} = 123;
$mdc{cde} = 234;

say '- Before ------------';

while( my($k,$v) = each %mdc ) {
    say "$k => $v";
}

mysub(\%mdc);

say '- After -------------';

while( my($k,$v) = each %mdc ) {
    say "$k => $v";
}

sub mysub {
  my $mdc_ref = shift;

  $mdc_ref->{def} = 345;
  $mdc_ref->{efg} = 456; 
}

Output

- Before ------------
cde => 234
abc => 123
- After -------------
def => 345
cde => 234
abc => 123
efg => 456

Upvotes: 0

Related Questions