1

I need to pass in a variable,list and a hash to a subroutine, and get back changes from it without returning.

This code doesnt do the job.

sub foo{
    my $one = {$_[0]};   
    my @list = @{$_[1]};
    my %hash = %{$_[2]};
    print("\n inside foo: ${one}\n");
    print("\n inside foo: $one @list $hash{'key'}\n");
    $one = 2;
    @list = (4,5,6);
    my %hash2;
    $hash2{'key'} = 'valueModified';    
    %hash = %hash2;
    print("\n inside foo after Mod: $one\n");
    print("\n inside foo after Mod: $one @list $hash{'key'}\n");
}

my $one = 1;
my @list = (1,2,3);
my %hash;
$hash{'key'} = 'value';
my @allLocalArgs = (\$one,\@list,\%hash);
foo(\@allLocalArgs);
print("\nafter foo modification: $one @list $hash{'key'}\n");

Output:

 inside foo: HASH(0x234a240)

 inside foo: HASH(0x234a240)  

 inside foo after Mod: 2

 inside foo after Mod: 2 4 5 6 valueModified

after foo modification: 1 1 2 3 value

Thanks.

zdim
  • 64,580
  • 5
  • 52
  • 81
ashishsony
  • 2,537
  • 3
  • 26
  • 38

1 Answers1

5

In principle, and in practically any language, you'd want to pass objects that refer to data structures in the caller, pointers or references. In Perl, that would be a reference -- and you are doing that.

But then in the sub you create local copies; the $one, @list, and %hash in the sub are lexical variables local to sub's scope, masking/shadowing the ones in the calling scope. Changes to those don't do anything outside of sub's scope.

Instead, directly use the references that you passed in order to write to the caller's data.

sub foo { 

    my ($rscalar, $rary, $rhash) = @_;

    $$rscalar = 2;
 
    @$rary = (4,5,6);

    $rhash->{'key'} = 'valueModified'; 
}

foo(\$one, \@list, \%hash);

Now $one, @list, and %hash in the calling code have been changed. For details of working with references please see tutorial perlreftut and reference perlref.

Note that foo(\@allLocalArgs); passes the reference of the array with arguments, so the sub receives one element -- that array's reference. You can do that but for the point of the question it isn't needed so I removed it and passed the list of arguments directly.

Another thing to note is that arguments are aliased in @_ -- so if in the sub you work with @_ directly then you may change data in the caller. So if you were to pass a scalar variable as foo($one) then $_[0] = 2 in the sub changes $one in the caller. This is in principle best avoided in my opinion; if caller's data is to be changed that should be made as explicit as possible; pass the reference.


A note on terminology

A list in Perl is an elusive, ephemeral structure used to move data around in a program; think of a bunch of scalars (values) on the stack somewhere, about to be used and disappear. Perhaps to pass arguments to a function (foo($v1, $v2)), or to form a string (join '', $v1, $v2), or to create an anonymous array reference ([$v1, $v2]), etc.

The other use of the term is for a syntax property, of a grouping/collection of scalars (usually seen in parenthesis, but those are incidental and needed for precedence really).

An array, on the other hand, is a multi-valued variable. Much like a hash (associative array) is a multi-valued variable, and as opposed to a scalar, being a single-valued variable.

A lot has been written on this; here are a few links that came up readily: an Effective Perler article (with links to a few more articles), and a Stackoverflow page Perl array vs list.

In short, you are asking about a "scalar, array, hash."

zdim
  • 64,580
  • 5
  • 52
  • 81
  • Speaking of lists and arrays (at the end), I recall an excellent [article on Mike Friedman ("friedo") blog](http://www.friedo.com/blog/2013/07/arrays-vs-lists-in-perl), but which is down right now. – zdim May 09 '19 at 18:31