coldmiser has asked for the wisdom of the Perl Monks concerning the following question:

I'm trying to get a grasp on references and I was under the impression that you could not pass hashes & arrays to a subroutine. I thought you needed to pass references to a subroutine and then dereference it once you were inside the sub.

To test this, I created this piece of code and my 'clear as mud' understanding of references became much more difficult to see through when my @array & %hash actually printed their values.

Can someone explain to me how I managed to do something I thought was not possible?

#! perl -w use strict; my @array = ("0","1","2","3"); my %hash = ("zero" => "0", "one" => "1", "two" => "2", "three" => "3") +; my $aref = \@array; my $href = \%hash; &test($aref, $href); sub test { my $one = shift; my $two = shift; print join (' ', @$one),"\n"; print join (' ', %$two),"\n"; print join (' ', @$aref),"\n"; print join (' ', %$href),"\n"; print join (' ', @array),"\n"; print join (' ', %hash),"\n"; sleep 1; }
Thanks.

Replies are listed 'Best First'.
Re: Small troubles with references
by wfsp (Abbot) on Feb 09, 2006 at 20:12 UTC
    Hi coldmiser!

    I agree with you, references can be tricky beasties to get the hang of.

    You successfully passed refs to your sub and de-referenced them. That's half the battle won already!

    But you can, in fact, pass an array or a hash to a sub. Change your test script to test it.

    The problem arises when, say, you want to pass two arrays.

    pass_arrays(@array_one, @array_two); sub pass_arrays{ my @a1 = @_; my @a2 = @_; }
    Perl will 'flatten' both arrays into one array and copy it into @a1. @a2 will be empty. If you pass refs:
    pass_arrays(\@array_one, \@array_two); sub pass_arrays{ my ($a1ref, $a2ref) = @_; }
    $a1ref and $a2ref now contain the refs you passed and you can use them as you did in your snippet.

    You might also write a script that changes the array/hash passed into a sub and then look at what they look like after the sub has been called.

    Your snippet also raises the question of scoping (as duff mentions). Have a look at Coping with Scoping to get more of an idea of what going on.

    You're nearly there, good luck!

      Greetings,
      Just to help illustrate how Perl 'flattens' or as I try to think of it (thanks to diotalevi) 'list-ifies' subroutine arguments consider the following
      #!/usr/bin/perl -w use strict; my @array = qw(arrayval1 arrayval2 arrayval3 arrayval4 arrayval5); my %hash = (key1=>'val1', key2=>'val2', key3=>'val3'); sub check_input_list { print "\n"; print "@_"; print "\n"; } check_input_list(@array, %hash); check_input_list(\@array, %hash); check_input_list(@array, \%hash); check_input_list(\@array, \%hash);
      The Output
      arrayval1 arrayval2 arrayval3 arrayval4 arrayval5 key2 val2 key1 val1 +key3 val3 ARRAY(0x1824320) key2 val2 key1 val1 key3 val3 arrayval1 arrayval2 arrayval3 arrayval4 arrayval5 HASH(0x182435c) ARRAY(0x1824320) HASH(0x182435c)
      Notice in the output of the first call the keys and values from %hash get tacked onto the end of the values in @array as if the entire thing were one huge list. Now imagine trying to get keys or values from %hash? Not likely nor easy.
      In the second and third examples we pass one reference but the argument list is still just a list.
      The final example is much cleaner. If you were confused about what you originally passed into the sub your could always call ref and find out; otherwise there is no way to tell if you are passed a 'list-ified' hash or array.

      -InjunJoel
      "I do not feel obliged to believe that the same God who endowed us with sense, reason and intellect has intended us to forego their use." -Galileo
Re: Small troubles with references
by MCS (Monk) on Feb 09, 2006 at 20:15 UTC

    The difference is that passing an array to a subroutine passes a copy of that array whereas a reference to an array you can dereference and get the actual array. Perhaps an example will help

    #! perl -w use strict; my @array = ("0","1","2","3"); my $aref = \@array; print "Original array: ", @array, "\n"; print "Pass original array to testnonref(): "; testnonref(@array); print @array, "\n"; print "Running sub passing array reference and printing array:"; testref($aref); print @array, "\n"; sub testnonref { my @subarray = shift; $subarray[0] = "4"; } sub testref { my $one = shift; $$one[0] = "4"; }

    Here I used your array and array reference (hases work the same way) and first I print out what the array looks like. Then I print out what the array looks like after passing it to the testnonref subroutine. Then I print out what it looks like after passing by reference. Hopefully the output will make it a little clearer:

    Original array: 0123
    Pass original array to testnonref(): 0123
    Running sub passing array reference and printing array:4123
    

    As you can see, passing by reference actually changes the value of your original array whereas just passing the array makes a copymakes a reference to your array using @_ and any changes are lost after the subroutine exits.

    Update: thanks, I always assumed (incorrectly) that perl copied the array like other languages. This is why I love perlmonks... always learning better ways of doing things.

      The difference is that passing an array to a subroutine passes a copy of that array

      Not true at all. @_ is aliased to all of the elements of the list passed to the subroutine. There is no copying going on unless you, the programmer, do it.

        A lot of newbies are unfamiliar with the distinction between an alias and a reference. And I think we should keep it that way. The fact that an alias is a reference that need not be dereferenced for use is pretty deep and dark voodoo that might confuse a beginner.

        :-)

        ---
        $world=~s/war/peace/g

Re: Small troubles with references
by duff (Parson) on Feb 09, 2006 at 19:44 UTC

    Of course @array and %hash printed their values, why wouldn't they? They were declared at file scope and are still visible inside of your subroutine.

      So then what's the advantage (or disadvantage of) using/creating references?
        Other people did a great job of answering pass-by-reference vs. pass-by-value. But if your question here is: "Why would you pass things to a subroutine when you can just call them by their original name?" then read on:

        #!/usr/bin/perl -w use strict; sub1(); sub sub1 { my @arr1 = ("dog", "cow", "camel"); print indexOf(\@arr1, "cow"); } sub indexOf { my $aref = shift; my $element = shift; my $count=0; foreach (@$aref) { return $count if($_ eq $element); $count++; } #foreach (@arr1) { } # WRONG, @arr1 is not in scope }


        As you can see in this example, you can't always call an element in the subroutine by the name from the caller. The variable might not be in scope in the callee.

        A final note: I'm using pass-by-reference here because I'm not modifying the array in the indexOf() subroutine. Pass-by-reference is faster and less memory consuming than pass-by-value, but be careful that the subroutine you pass to doesn't modify your contents if you're expecting them preserved.
Re: Small troubles with references
by Cody Pendant (Prior) on Feb 09, 2006 at 23:49 UTC