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

Any arguments passed in show up in the array @_. Therefore, if you called a function with two arguments, those would be stored in $_[0] and $_[1]. The array @_ is a local array, but its elements are aliases for the actual scalar parameters. In particular, if an element $_[0] is updated, the corresponding argument is updated (or an error occurs if it is not updatable). If an argument is an array or hash element which did not exist when the function was called, that element is created only when (and if) it is modified or a reference to it is taken. (Some earlier versions of Perl created the element whether or not the element was assigned to.) Assigning to the whole array @_ removes that aliasing, and does not update any arguments.

reading above paragraph from perlsub, and i don't really quite get this sentence..

If an argument is an array or hash element which did not exist when the function was called, that element is created only when (and if) it is modified or a reference to it is taken.

does it mean that the following should print 1 (but it doesn't)? perl -le 'x(@y);sub x{$_[0]=1;print @y}'

Replies are listed 'Best First'.
Re: perlsub question..
by shmem (Chancellor) on Jun 23, 2007 at 07:47 UTC
    does it mean that the following should print 1 (but it doesn't)? perl -le 'x(@y);sub x{$_[0]=1;print @y}'
    No, rather
    perl -le 'x($y[1]);sub x{$_[0]=1;print $c++,": $_" for @y}' 0: 1: 1

    In your version calling x(@y) flattens the (empty) array @y into an empty list. There's no element of @y to be aliased and passed within @_ into x().

    Note that indexing an array does not create slots within:

    perl -le '$c = $y[10]; x($y[1]);sub x{$_[0]=1;print $c++,": $_" for @y +}' 0: 1: 1

    The array @y isn't 11 elements long merely because I looked at index 10. The sentence you asked about describes that for subroutines. An argument to a subroutine can be a nonexistent element of an array or hash (not an array or hash), and it only springs into existence if told so via its alias inside the sub (by assigning or referencing).

    --shmem

    _($_=" "x(1<<5)."?\n".q·/)Oo.  G°\        /
                                  /\_¯/(q    /
    ----------------------------  \__(m.====·.(_("always off the crowd"))."·
    ");sub _{s./.($e="'Itrs `mnsgdq Gdbj O`qkdq")=~y/"-y/#-z/;$e.e && print}
      thank you. that makes sense. few lines down in perlsub, i also read this paragraph:

      If no return is found and if the last statement is an expression, its +value is returned. If the last statement is a loop control structure +like a foreach or a while, the returned value is unspecified. The emp +ty sub returns the empty list.

      the last two sentences look wrong to me. tried this:

      D:\>perl -MData::Dumper -e "sub x{1 for 1..5};@y=x();print 'ha' if def +ined @y" ha

      so the return value is certainly defined.

      The empty sub returns the empty list. that depends on what it's return context is though, not necessarily a empty list..

        the last two sentences look wrong to me. tried this:
        D:\>perl -MData::Dumper -e "sub x{1 for 1..5};@y=x();print 'ha' if def +ined @y" ha
        There's a few things to note:
        • It's said that If the last statement is a loop control structure like a foreach or a while, the returned value is unspecified. "Unspecified" is not equal to "undefined", it means Perl can't guarantee what such subs would return. But in your case, it returns a list with a single empty element.
          $ perl -MData::Dumper -e 'sub x{1 for 1..5};@y=x();print Dumper(\@y)' $VAR1 = [ '' ];
        • You don't use defined function for aggregate variables such as arrays and hashes because, Use of "defined" on aggregates (hashes and arrays) is deprecated. It used to report whether memory for that aggregate has ever been allocated. This behavior may disappear in future versions of Perl. So, it prints "ha" when you check the defined-ness of the array @y because it has been allocated some memory.
        • Most (if not all) of the time you want to check whether an array or a hash have element, thus say ... if @y; instead of ... if defined @y;. Eventhough, it would still print "ha" because the array has, indeed, an element which is empty (see point 1).
        Finally, consider the following snippets:
        $ perl -wle 'sub x{};@y=x(); print "array" if @y' (nothing) $ perl -wle 'sub x{};%y=x();print "hash" if %y' (nothing) $ perl -wle 'sub x{1..3};@y=x(); print "array" if @y' array $ perl -wle 'sub x{a => 1};%y=x();print "hash" if %y' hash

        Open source softwares? Share and enjoy. Make profit from them if you can. Yet, share and enjoy!

        The docs did not say the returned value is undefined, they say it's unspecified! It means that you should not expect the value to make any sense, it could be anything. You could get the 1 you got, you could get an undef, you could get an 'gotcha dude!' string, you could get anything. The result is not specified.

        What do you load Data::Dumper for? Had you used it, you had found:

        perl -e "sub x{ 1 for 1..5 };@y=x(); print Dumper(\@y) if defined @y" $VAR1 = [ '' ];

        Looks pretty much "unspecified". Finding out what that sub actually returned is left as an exercise to the reader :-)

        that depends on what it's return context is though, not necessarily a empty list..
        Yes. Return values are evaluated in the caller's context at subroutine return, which may happen implicitly at the subroutine end.

        No, subs always return a list. Sometimes it's empty, sometimes it has only one value. It's up to the caller how it treats that list, and it's up to the sub which elements it returns if it evaluates the context in which it is executed..

        This discussion makes me marvel again upon how well written the perl documentation is...

        --shmem

        _($_=" "x(1<<5)."?\n".q·/)Oo.  G°\        /
                                      /\_¯/(q    /
        ----------------------------  \__(m.====·.(_("always off the crowd"))."·
        ");sub _{s./.($e="'Itrs `mnsgdq Gdbj O`qkdq")=~y/"-y/#-z/;$e.e && print}
Re: perlsub question..
by GrandFather (Saint) on Jun 23, 2007 at 04:17 UTC

    it means:

    use strict; use warnings; my @array = (0, 1, 2); untouched ($array[3]); print exists $array[3] ? "Element exists" : "No element"; touch ($array[3]); print "\n", exists $array[3] ? "Element exists" : "No element"; sub untouched { } sub touch { $_[0] = 1; }

    Prints:

    No element Element exists

    because the element at index 3 didn't exist until it was referenced in touch.

    Update to answer actual question


    DWIM is Perl's answer to Gödel
Re: perlsub question..
by guinex (Sexton) on Jun 23, 2007 at 04:10 UTC
    use Data::Dumper; sub foo { $_[0] = undef; } sub bar {}; my @a; @a = (); foo( $a[0] ); print Dumper \@a; @a = (); bar( $a[0] ); print Dumper \@a;
    This prints:
    $VAR1 = [ undef ]; $VAR1 = [];

    As I read the paragraph you quoted, some earlier versions of perl would have produced identical output in both cases.

    -guinex

Re: perlsub question..
by NetWallah (Canon) on Jun 23, 2007 at 15:00 UTC
    The behaviour I see, on "perl, v5.8.8 built for MSWin32-x86-multi-thread" is not consistent with the documentation the OP posted.
    > perl -e "my @y;$y[3]=undef; show(@y); x(@y);show(@y);sub show{my $co +unt=0;print $count++, qq[ : $_;\n] for @_;print qq[----\n\n]}; sub x +{ @_[3,4,5,6,8]=(3,4,5,6,939);show(@_)}" --output-- 0 : ; 1 : ; 2 : ; 3 : ; ---- 0 : ; 1 : ; 2 : ; 3 : 3; 4 : 4; 5 : 5; 6 : 6; 7 : ; 8 : 939; ---- 0 : ; 1 : ; 2 : ; 3 : 3; ----
    I would document the observed behaviour as :
    Array elements are NOT created. Existing elements (which were EXPLICITLY created) will retain modifification in the sub.

         "An undefined problem has an infinite number of solutions." - Robert A. Humphrey         "If you're not part of the solution, you're part of the precipitate." - Henry J. Tillman

      You can't pass an array to a subroutine! You can pass the values of an array (which is what you did) or a reference to an array. Consider this:

      sub foo { print "@_\n"; push @_, 99; } @a = (1,2,4); foo(@a, 666)

      Now what's Perl supposed to do with this? Keep in mind that the foo() did not get two parameters, it got FOUR. The three elements of @a and the 666. The first three modifiable, the last readonly. No array was passed.

      The docs talk about things like foo( $a[99]) and foo( $h{new_key}), where in an old version the @a would be expanded and the 'new_key' key added into the %h upon calling the subroutine, while in the new versions this is only done if necessary.

      This is consistent with things like if (defined($a[99]){... versus $a[99] = 0;.

        Got it (++).

        So, would this statement be right :
        Elements of @_ are aliased to EXISTING (I was going to say DEFINED, but elements with the value 'undef' are updatable) elements of the passed parameter.

        This would explain why the indexes 5,6 and 8 never get created in the code I posted.

             "An undefined problem has an infinite number of solutions." - Robert A. Humphrey         "If you're not part of the solution, you're part of the precipitate." - Henry J. Tillman

Re: perlsub question..
by ikegami (Patriarch) on Jun 24, 2007 at 17:49 UTC

    You can't pass an array to a function.
    If @a has 0 elements, func(@a) is the same as func().
    If @a has 1 element, func(@a) is the same as func($a[0]).
    If @a has 2 elements, func(@a) is the same as func($a[0], $a[1]).
    If @a has 3 elements, func(@a) is the same as func($a[0], $a[1], $a[2]).
    etc.

    That's why the text speaks of an "array or hash element".

    sub test1 { $a = $_[0]; } sub test2 { $_[0] = $a; } my @a; test1($a[0]); print(scalar(@a), "\n"); # 0 test2($a[0]); print(scalar(@a), "\n"); # 1

    Update: Ah shoot! It had written $_[x] where I had meant to write $a[x]. Fixed. Thanks ysth and graff.

      ... is the same as ...
      But it's not. It's more like
      func(map exists($a[$_]) ? $a[$_] : undef, 0..$#a)
      (only that map breaks the binding of undefined but existing elements in some way that func(@a) doesn't).
Re: perlsub question.. ("alias"?!)
by tye (Sage) on Jun 24, 2007 at 16:40 UTC

    I object to this wording. You can't have an alias to something that doesn't exist. What is really happening is that, in older Perl, passing a member of an aggregate (an array element or a hash value) forced it to be created so that it could be aliased and the alias put into @_ (because sub call arguments are an lvalue context just like other lvalue context that might not actually change the variable you place in them). In this new Perl, instead a "magic" scalar is created and modifying this magic scalar will cause a new scalar to be inserted into the aggregate (if not already inserted by some other operation) and then cause the modified value to be copied over that member. At no point is this magic placeholder scalar an alias to the potential aggregate member (I bet).

    I don't have a bleeding-edge Perl so I can't test, but I can already think of quite a few ways in which this change likely significantly modifies existing behavior (not to mention the ways in which it certainly modifies long-standing behavior). Nothing like throwing in some complex "magic" to break backward compatability and make really explaining lvalue context a lot more complicated for the sake of preventing a minor newbie surprise. And why, again, is it that we can't make <>'s use of @ARGV not be completely stupid? I guess doing so doesn't require enough complicated magic in order to make it appealing to p5p.

    At least once in this documentation the term "alias" should be replaced with "magic 'alias'".

    - tye