Beefy Boxes and Bandwidth Generously Provided by pair Networks
Clear questions and runnable code
get the best and fastest answer
 
PerlMonks  

Re: Accessing Arguments inside Subroutines via @_

by Athanasius (Archbishop)
on Mar 20, 2015 at 02:53 UTC ( [id://1120688]=note: print w/replies, xml ) Need Help??


in reply to Accessing Arguments inside Subroutines via @_

Hello citi2015, and welcome to the Monastery!

The final sentence of the paragraph in perlsub you quote is this:

Assigning to the whole array @_ removes that aliasing, and does not update any arguments.

This was new to me. It answers your question, but I would be interested to know what motivated the design decision behind it.

Hope that helps,

Athanasius <°(((><contra mundum Iustus alius egestas vitae, eros Piratica,

Replies are listed 'Best First'.
Re^2: Accessing Arguments inside Subroutines via @_
by LanX (Saint) on Mar 20, 2015 at 03:07 UTC
    > I would be interested to know what motivated the design decision behind it.

    I think it follows the normal inner mechanism.

    The elements are aliases not the array holding them.

    Think about how shift @_ deletes an alias.

    One might argue that the array should not be writable, but its sometimes necessary to rewrite @_ before doing a goto &sub .

    Cheers Rolf
    (addicted to the Perl Programming Language and ☆☆☆☆ :)

    PS: Je suis Charlie!

Re^2: Accessing Arguments inside Subroutines via @_
by LanX (Saint) on Mar 21, 2015 at 12:43 UTC

    I think what is really troubling you is the ambiguity between setting a symbol's slot and assigning to a symbol in Perl. (not sure if this is proper terminology)

    Pure Perl has neither an explicit alias operator, nor an explicit unalias operator.²

    So if a scalar variable $a is an alias to $b you can't easily say $a=42 without changing $b , i.e. replacing the alias in the $a -"slot" with a literal 42.¹

    But when operating with arrays like @_ you have the choice.

    $_[0]=42 is accessing the alias behind, but @_=(42) will replace the content of the slot.

    DB<106> sub repl { @_=(42); print "> $_[0]"; } DB<107> $a=666 => 666 DB<108> repl $a > 42 DB<109> $a => 666

    It has already been shown how to access multiple aliases at once (slicing), similarly you can use splice to access multiple slots of an array at once.

    I hope the distinction between setting a slot and assigning to an alias is clearer now. :)

    Cheers Rolf
    (addicted to the Perl Programming Language and ☆☆☆☆ :)

    PS: Je suis Charlie!

    ¹) But you can always use my $a=42 in a new scope or local $a=42 if it's a package var.

    ²) like tie and untie

      Hello LanX,

      I think what is really troubling you is the ambiguity between setting a symbol's slot and assigning to a symbol in Perl.

      I’m not sure if I understand the distinction you’re making here, but my current understanding of a Perl alias is that it’s in some ways analogous to a smart pointer. That is, it has its own identity as a scalar value (of type “alias”), but under certain conditions it behaves “transparently” as the entity it aliases, like a dereferenced pointer. To explain what I mean: I was experimenting with various permutations of the following code:

      and the output shows that following unshift @_ the element $_[1] is now an alias for the first argument. So, when explaining how @_ functions in a subroutine, I would no longer say “$_[0] is an alias for the first argument,” but rather “$_[0] is initialised to a (scalar value which is an) alias for the first argument.”

      And the difference in behaviour between, e.g., @_ = reverse @_ and ( $_[0], $_[1] ) = ( $_[1], $_[0] ), comes down to whether the aliases act “transparently” or ”opaquely.” When elements of @_ are accessed individually — whether singly or as part of an array slice — they behave “transparently,” but when the array @_ is assigned-to — via = or splice — its alias values are accessed “opaquely,” meaning their referents remain unaffected.

      Well, that’s my current understanding, and it may be just a convoluted way of repeating your explanation in different words. Anyway, I think I now understand what’s going on a little better than I did before. :-)

      Update: Fixed typo.

      Thanks for the help,

      Athanasius <°(((><contra mundum Iustus alius egestas vitae, eros Piratica,

        Well you asked for the "why" not for the "how" ... :)

        I took a look into the Panther book and experimented with Devel::Peek and the implementation of aliases seems simpler than a magic data structure of type alias.

        Arrays (AVs) are composed from C-structures with pointers to C-structure(s) realizing scalars (SVs).

        So $_[0]=666 updates the underlying scalar ( what I called "assigning" ) while @_=(666) creates the pointer to a new scalar ( "setting" ).

        In the following dump the scalar value for $x is held within SV = IV(0x88f2498) at 0x88f249c

        The array's first slot will point to that same structure before and after updating with $_[0]=666 (#markers added)

        But creating new entries with @_=(666) will replace the pointer in this AV-slot to a new SV.

        I hope the "how" is clearer now! :)

        Cheers Rolf
        (addicted to the Perl Programming Language and ☆☆☆☆ :)

        PS: Je suis Charlie!

      Pure Perl has neither an explicit alias operator

      You can (ab)use typeglobs for this, at least for package variables. For instance:

      #!/usr/bin/perl no strict; use warnings; use feature qw/say/; $a = 42; *b = *a; say $b; # 42 $b = 69; say $a; # 69

      I can't think of a way of doing this with lexicals off the top of my head, though.

      nor an explicit unalias operator.²

      So if a scalar variable $a is an alias to $b you can't easily say $a=42 without changing $b , i.e. replacing the alias in the $a -"slot" with a literal 42.¹

      I don't think this is needed. Suppose that you have variables - let's say lexicals - $a and $b, with the latter being aliased to the former. Unalias $a would mean creating a new lexical that's not related to $b anymore, so why not just declare a new lexical with the same value (my $c = $b) and use that instead? The result's the same, and it's arguably clearer, since if you look at any line in isolation there's no confusion over whether $a is (still) aliased to $b anymore.

      OTOH there's something to be said in favor of explicit alias and unalias operators as well. I'd not be surprised at all if there were CPAN modules that implemented this.

        I just wanted to highlight why it makes sense that assigning to @_ doesn't write thru to the aliases.

        my and local were already mentioned in my footnote, when using typeglobs I'd strongly suggest to prefer *b = \$a; over  *b = *a;

        But these mechanisms are no big help if you want named parameters in subs cause those are lexicals, (which should be the preferred variable flavor).

        > I'd not be surprised at all if there were CPAN modules that implemented this.

        see Data::Alias or Lexical::Alias and their discussion in PBP

        Perl6 aims to handle aliasing consistently.

        Cheers Rolf
        (addicted to the Perl Programming Language and ☆☆☆☆ :)

        PS: Je suis Charlie!

Re^2: Accessing Arguments inside Subroutines via @_
by citi2015 (Initiate) on Mar 20, 2015 at 03:07 UTC

    Thanks a lot, Athanasius.

    No design decision behind it. A newer read that code from one book and find this error, I just realise I cannot explain. so I do my test and post the question on the community.

    Thanks again for the help.

      Hello citi2015,

      I meant Perl’s design decision to remove the aliasing when @_ is assigned-to within a subroutine.

      You can, of course, do the swap by explicitly accessing the subroutine arguments as individual elements of @_:

      sub swap { my $temp = $_[0]; $_[0] = $_[1]; $_[1] = $temp; }

      — but note the necessity of “remembering” the initial value of $_[0] by storing it in a temporary variable. And I think that answers my question: if the aliasing were not removed, @_ = reverse @_ would produce wrong results, because some elements would be changed (assigned-to) before they were assigned-from.

      Hope that helps,

      Athanasius <°(((><contra mundum Iustus alius egestas vitae, eros Piratica,

        Sorry, your theory is wrong.

        A list assignment doesn't need a $temp var and there is no race condition.

        DB<100> sub rev { ($_[0],$_[1]) = ($_[1],$_[0]) } DB<101> @a=("A","B") => ("A", "B") DB<102> rev @a DB<103> @a => ("B", "A")

        Cheers Rolf
        (addicted to the Perl Programming Language and ☆☆☆☆ :)

        PS: Je suis Charlie!

        Since perl can do multiple assignments in one swell foop, this would work too, avoiding the temporary variable:

        sub swappy { ($_[0], $_[1]) = ($_[1], $_[0]) }

        ...roboticus

        When your only tool is a hammer, all problems look like your thumb.

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: note [id://1120688]
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others admiring the Monastery: (4)
As of 2024-04-19 00:45 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found