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

Esteemed Perl Monks;
I am trying to do something akin to the following;
thing(@\FooBar); for (@{$FooBar[0]}) { print "$_\n"; } for (@{$FooBar[1]}) { print "$_\n"; } sub thing() { ($REF_array)=@_; @Array=@{$REF_array}; @Array[0]=([some,values]); @Array[1]=([some,more,values]); }
And I'm expecting it to print;
some
values
some
more
values


This reference / dereference stuff in perl really hurts my head, and looks ugly! ;P

Thanks for your time,
matt


Hmm, does anyone know historically why perl uses references to pass arrays etc.. to subroutines? and why you can only return one value from a subroutine?

Replies are listed 'Best First'.
Re: I've read perlref
by Corion (Patriarch) on Mar 11, 2004 at 11:26 UTC

    You can easily return multiple values from a subroutine - it must be some weird other language you're thinking about, like C or Pascal, that can only return one value and not a list from a subroutine.

    You can also pass in lists to a subroutine, but Perl automatically flattens all lists into one big list, and you lose the information which element came from one list:

    use strict; sub foo { my (@args) = @_; print "$_\n" for @args; }; my @a = qw(a1 a2 a3); my @b = qw(b1 b2 b3 b4 b5); foo(1,2,3,4,5,@a,@b);

    If you want to keep the distinction between the arrays, it is customary to pass the arrays via reference :

    use strict; sub foo { my ($val1,$val2,$arr_ref1,$arr_ref2) = @_; print "$_\n" for ($val1,$val2,$arr_ref1,$arr_ref2); }; my @a = qw(a1 a2 a3); my @b = qw(b1 b2 b3 b4 b5); foo(1,2,\@a,\@b);

    To get at the values in @a and @b, you'll need to dereference $arr_ref1 and $arr_ref2:

    use strict; sub foo { my ($val1,$val2,$arr_ref1,$arr_ref2) = @_; print "$_\n" for ($val1,$val2); for my $array ($arr_ref1,$arr_ref2) { for my $element (@$array) { print "array: $element\n"; }; }; }; my @a = qw(a1 a2 a3); my @b = qw(b1 b2 b3 b4 b5); foo(1,2,\@a,\@b);

    See also tyes References quick reference for a much more and better explanation.

Re: I've read perlref
by matija (Priest) on Mar 11, 2004 at 11:46 UTC
    When you are experimenting with new things, always run with -w ON:
    Scalar value @Array[0] better written as $Array[0] at /tmp/t line 17. Unquoted string "some" may clash with future reserved word at /tmp/t l +ine 17. Scalar value @Array[1] better written as $Array[1] at /tmp/t line 18. Unquoted string "some" may clash with future reserved word at /tmp/t l +ine 18. Unquoted string "more" may clash with future reserved word at /tmp/t l +ine 18. main::thing() called too early to check prototype at /tmp/t line 2. Not enough arguments for values at /tmp/t line 17, near "values]" Not enough arguments for values at /tmp/t line 18, near "values]"
    But I think your biggest problem is the first line of thing, where you say:
    ($REF_array)=@_;
    That makes a copy of the array. You are no longer assigning to the references to the original arguments, and thus your values are lost once you get out of the subroutine...

    I think what you want is more akin to this:

    sub thing() { ${$_[0]}[0]=([qw(some values)]); ${$_[0]}[1]=([qw(some more values)]); }
      ($REF_array)=@_;
      That makes a copy of the array. You are no longer assigning to the references to the original arguments, and thus your values are lost once you get out of the subroutine...

      Technically that makes a copy of the array reference. The @Array = @{$REF_array} is what makes a copy of the array referenced. The rest of your diagnostic is correct (the original arrayref is untouched by modifications to @Array). </pedant>

Re: I've read perlref
by maa (Pilgrim) on Mar 11, 2004 at 11:27 UTC
    and why you can only return one value from a subroutine?

    You can return as many values as you like from a sub in list context. How do you think split etc work?

    why perl uses references to pass arrays?

    Because it's easier... and more efficient. It's also the best way to return multple arrays from a sub... return \@a1, \@a2;

    UPDATE: just noticed that you have not QUOTED your array values... and values is a perl keyword!

      why perl uses references to pass arrays?
      Because it's easier... and more efficient.
      Well, it's certainly not easier as you need to explicitely put a reference in (and later dereference the reference). I also doubt this would be more efficient if direct passing of arrays was available in Perl.

      The real reason is that there's no way in Perl to directly pass an array - even with a @ prototype, you're still passing a reference. Perl flattens an array into a list when used in list context. That's the reason we use references (or globs, but that's very perl4-ish) to pass arrays.

      Abigail

        yesyes, I understand the efficiency argument (copying data rather than pointing to it), but it certainly is not easier, or quicker to develop.

        And yes, you can only pass one value from a subroutine, even if that value is a list of other values. Joy. Rather than being able to directly return a int, a list, a different list I have to stuff all that into onebig list and start playing with references all over again!

        Why they want me to do this stuff in Perl and not Python I dont know....
Re: I've read perlref
by Anonymous Monk on Mar 11, 2004 at 11:16 UTC
    Oops
    The first line of code should read
    thing(\@FooBar);

    If you hadnt guessed...
Re: I've read perlref
by eric256 (Parson) on Mar 11, 2004 at 16:36 UTC

    Here is that same code rewritten to produce your desired output. you went a little nuts adding in () where they were not needed. I changed the first line of thing so that it users shift, this makes it clear its grabbing the first value passed to the sub. Your way worked but it was less clear in my opinion. Then the rest of thing is the same except you use $REF_array-> in place of @ARRAY and drop the () around the values on the right. I'm not sure what there purpose was there but the code runs without them. Without further ado....

    use warnings; thing(\@FooBar); for (@{$FooBar[0]}) { print "$_\n"; } for (@{$FooBar[1]}) { print "$_\n"; } sub thing { $REF_array = shift; $REF_array->[0] = ["some","values"]; $REF_array->[1] = ["some","more","values"]; }


    ___________
    Eric Hodges