Why does assigning @_ cause sub's parameter to be copied?

Discussion in 'Perl Misc' started by Jerry Krinock, Mar 7, 2010.

  1. After several hours of head-scratching, I finally found out why I
    changes I made to an array passed by reference to a subroutine are not
    seen outside the subroutine, thanks to Steve Litt's Perls of Wisdom
    [1]:

    "Any change the subroutine performs to @_ or any of its members like
    $_[0], $_[1], etc, are changes to the original argument. HOWEVER,
    assigning @_ or its elements to other variables makes a separate copy.
    Changes to the separate copy are unknown outside of the subroutine."

    Maybe if someone could explain the reason why Perl would do this, make
    a copy upon *assignment*, it would be easier for me to remember. Is
    this a "feature" of the language, or some unfortunate implementation
    reality? The reason why I discovered this is that since I'm not a
    full-time Perl guy, I try and write readable code, defining lots of
    variables. However, as you can see from my code below, my nice and
    pedantic subroutine pushBad() doesn't work because it makes a local
    assignment, but the hard-to-read pushGood() works.

    Thanks for any insights!

    Jerry Krinock

    1. http://www.troubleshooters.com/codecorn/littperl/perlsub.htm.
    Steve apparently knows it well; he repeated that text in six places on
    this page :))

    #!/usr/bin/perl
    use strict ;

    my @array = ("start") ;
    pushBad(\@array) ;
    print("Passed back from pushBad: @array\n") ;
    pushGood(\@array) ;
    print("Passed back from pushGood: @array\n") ;

    sub pushBad {
    my $arrayRef = shift ;
    my @array = @$arrayRef ;
    push (@array, "bad") ;
    print("After pushing, in pushBad: @array\n") ;
    }

    sub pushGood {
    push(@{$_[0]}, "good") ;
    print("After pushing, in pushGood: @{$_[0]}\n") ;
    }

    CONSOLE OUTPUT:
    After pushing, in pushBad: start bad
    Passed back from pushBad: start
    After pushing, in pushGood: start good
    Passed back from pushGood: start good
    Jerry Krinock, Mar 7, 2010
    #1
    1. Advertising

  2. Jerry Krinock <> wrote:
    >"Any change the subroutine performs to @_ or any of its members like
    >$_[0], $_[1], etc, are changes to the original argument. HOWEVER,
    >assigning @_ or its elements to other variables makes a separate copy.
    >Changes to the separate copy are unknown outside of the subroutine."
    >
    >Maybe if someone could explain the reason why Perl would do this, make
    >a copy upon *assignment*, it would be easier for me to remember.


    Imagine the following code:
    @foo = qw 'Original content of foo';
    @bar = @foo;
    @bar = qw 'New content';
    Now, which content do you expect @foo to have?

    >sub pushBad {
    > my $arrayRef = shift ;
    > my @array = @$arrayRef ;
    > push (@array, "bad") ;
    > print("After pushing, in pushBad: @array\n") ;
    >}


    And your sample code here is exactly the same as my snippet above except
    that instead of an array @foo you got an array @$arrayRef, i.e. one
    level of reference. But otherwise they do the same thing: assigning an
    array to another array and there is no reason why changing the elements
    in the second array should be mirrored in the original array.

    jue
    Jürgen Exner, Mar 7, 2010
    #2
    1. Advertising

  3. Jerry Krinock

    Uri Guttman Guest

    >>>>> "JK" == Jerry Krinock <> writes:

    JK> 1. http://www.troubleshooters.com/codecorn/littperl/perlsub.htm.
    JK> Steve apparently knows it well; he repeated that text in six places on
    JK> this page :))

    he knows what well? that page is ancient perl style. why are you
    learning from it instead of a good book or the perl docs? almost every
    perl tutorial on the web is poorly written, inaccurate, buggy and
    worse. i have reviewed dozens and they all seem to be similarly bad. the
    fact that this page shows &foo style calls is indicative of its low
    quality.

    I have been unable to hack into a subroutine via its scalar
    return. If you know of a way it can be done, please let me know,
    as this would be a horrid violation of encapsulation.

    what kind of a moronic statement is that? regardless of whether it can
    be done or not, it is just dumb to write that.

    Returning a List

    sub getFnameLname
    {
    return("Bill", "Clinton");
    }

    that only returns a list in list context. no use describing sub returns
    without covering context as well. typical bad web tute stuff.

    Returning a Hash

    sub getOfficers
    {
    return("president"=>"Bill Clinton",
    "vice president"=>"Al Gore",
    "intern"=>"Monica Lewinsky"
    );

    that isn't actually returning a hash which can't be done. it is
    returning a list (assuming list context as mentioned above) and it may
    be assigned to a hash whereby that list will be converted to a hash.

    Arguments to a subroutine are accessible inside the subroutine
    as list @_. Any change the subroutine performs to @_ or any of
    its members like $_[0], $_[1], etc, are changes to the original
    argument. HOWEVER, assigning @_ or its elements to other
    variables makes a separate copy. Changes to the separate copy
    are unknown outside of the subroutine.

    what a maroon! the usual idiom is always assigning @_ to
    lexicals. instead he starts off with call by reference stuff which is
    rarely used. passing real refs is the correct and safe way to do
    that. ugh.


    JK> #!/usr/bin/perl
    JK> use strict ;

    JK> my @array = ("start") ;
    JK> pushBad(\@array) ;
    JK> print("Passed back from pushBad: @array\n") ;
    JK> pushGood(\@array) ;
    JK> print("Passed back from pushGood: @array\n") ;

    JK> sub pushBad {
    JK> my $arrayRef = shift ;

    that is still the original array ref

    JK> my @array = @$arrayRef ;

    HERE you copied the array to a lexical. all changes to @array will
    remain there.
    JK> push (@array, "bad") ;

    why would you expect this to change the original array? what possible
    way would you think that would happen? you COPIED the array, not a reference.

    JK> print("After pushing, in pushBad: @array\n") ;
    JK> }



    JK> sub pushGood {
    JK> push(@{$_[0]}, "good") ;
    JK> print("After pushing, in pushGood: @{$_[0]}\n") ;
    JK> }

    and that is the bad way to pass by reference.

    uri

    --
    Uri Guttman ------ -------- http://www.sysarch.com --
    ----- Perl Code Review , Architecture, Development, Training, Support ------
    --------- Gourmet Hot Cocoa Mix ---- http://bestfriendscocoa.com ---------
    Uri Guttman, Mar 7, 2010
    #3
  4. Jerry Krinock

    Frank Seitz Guest

    Jerry Krinock wrote:
    > After several hours of head-scratching, I finally found out why I
    > changes I made to an array passed by reference to a subroutine are not
    > seen outside the subroutine, thanks to Steve Litt's Perls of Wisdom
    > [1]:
    >
    > "Any change the subroutine performs to @_ or any of its members like
    > $_[0], $_[1], etc, are changes to the original argument. HOWEVER,
    > assigning @_ or its elements to other variables makes a separate copy.
    > Changes to the separate copy are unknown outside of the subroutine."
    >
    > Maybe if someone could explain the reason why Perl would do this, make
    > a copy upon *assignment*, it would be easier for me to remember. Is
    > this a "feature" of the language, or some unfortunate implementation
    > reality? The reason why I discovered this is that since I'm not a
    > full-time Perl guy, I try and write readable code, defining lots of
    > variables. However, as you can see from my code below, my nice and
    > pedantic subroutine pushBad() doesn't work because it makes a local
    > assignment, but the hard-to-read pushGood() works.
    >
    > Thanks for any insights!
    >
    > Jerry Krinock
    >
    > 1. http://www.troubleshooters.com/codecorn/littperl/perlsub.htm.
    > Steve apparently knows it well; he repeated that text in six places on
    > this page :))
    >
    > #!/usr/bin/perl
    > use strict ;
    >
    > my @array = ("start") ;
    > pushBad(\@array) ;
    > print("Passed back from pushBad: @array\n") ;
    > pushGood(\@array) ;
    > print("Passed back from pushGood: @array\n") ;
    >
    > sub pushBad {
    > my $arrayRef = shift ;
    > my @array = @$arrayRef ;
    > push (@array, "bad") ;
    > print("After pushing, in pushBad: @array\n") ;
    > }
    >
    > sub pushGood {
    > push(@{$_[0]}, "good") ;
    > print("After pushing, in pushGood: @{$_[0]}\n") ;
    > }


    Readable call by reference code in Perl:

    sub pushGood {
    my $arrayRef = shift;
    push(@$arrayRef, "good");
    print("After pushing, in pushGood: @$arrayRef\n") ;
    }

    Frank
    --
    Dipl.-Inform. Frank Seitz
    Anwendungen für Ihr Internet und Intranet
    Tel: 04103/180301; Fax: -02; Industriestr. 31, 22880 Wedel

    Blog: http://www.fseitz.de/blog
    XING-Profil: http://www.xing.com/profile/Frank_Seitz2
    Frank Seitz, Mar 7, 2010
    #4
  5. Jerry Krinock

    sreservoir Guest

    On 3/7/2010 1:35 AM, Jerry Krinock wrote:
    > After several hours of head-scratching, I finally found out why I
    > changes I made to an array passed by reference to a subroutine are not
    > seen outside the subroutine, thanks to Steve Litt's Perls of Wisdom
    > [1]:
    >
    > "Any change the subroutine performs to @_ or any of its members like
    > $_[0], $_[1], etc, are changes to the original argument. HOWEVER,
    > assigning @_ or its elements to other variables makes a separate copy.
    > Changes to the separate copy are unknown outside of the subroutine."


    never do this. not unless you have a really good idea of why you're
    doing it. you obviously don't.

    > Maybe if someone could explain the reason why Perl would do this, make
    > a copy upon *assignment*, it would be easier for me to remember. Is
    > this a "feature" of the language, or some unfortunate implementation
    > reality? The reason why I discovered this is that since I'm not a
    > full-time Perl guy, I try and write readable code, defining lots of
    > variables. However, as you can see from my code below, my nice and
    > pedantic subroutine pushBad() doesn't work because it makes a local
    > assignment, but the hard-to-read pushGood() works.


    try this:

    push(my @b = @{my $a = [qw/this list/]}, qw/that list/);

    what would you expect $a to contain?

    it seems you expect [qw/this list that list/].

    I prefer, in perls above 5.6, to use subroutine prototypes:

    sub pushScalar(\@$) {
    my $ref = shift;
    push @$ref, shift;
    }
    --

    "Six by nine. Forty two."
    "That's it. That's all there is."
    "I always thought something was fundamentally wrong with the universe"
    sreservoir, Mar 7, 2010
    #5
  6. Jerry Krinock

    sreservoir Guest

    On 3/7/2010 10:36 AM, sreservoir wrote:
    > On 3/7/2010 1:35 AM, Jerry Krinock wrote:
    >> After several hours of head-scratching, I finally found out why I
    >> changes I made to an array passed by reference to a subroutine are not
    >> seen outside the subroutine, thanks to Steve Litt's Perls of Wisdom
    >> [1]:
    >>
    >> "Any change the subroutine performs to @_ or any of its members like
    >> $_[0], $_[1], etc, are changes to the original argument. HOWEVER,
    >> assigning @_ or its elements to other variables makes a separate copy.
    >> Changes to the separate copy are unknown outside of the subroutine."

    >
    > never do this. not unless you have a really good idea of why you're
    > doing it. you obviously don't.
    >
    >> Maybe if someone could explain the reason why Perl would do this, make
    >> a copy upon *assignment*, it would be easier for me to remember. Is
    >> this a "feature" of the language, or some unfortunate implementation
    >> reality? The reason why I discovered this is that since I'm not a
    >> full-time Perl guy, I try and write readable code, defining lots of
    >> variables. However, as you can see from my code below, my nice and
    >> pedantic subroutine pushBad() doesn't work because it makes a local
    >> assignment, but the hard-to-read pushGood() works.

    >
    > try this:
    >
    > push(my @b = @{my $a = [qw/this list/]}, qw/that list/);
    >
    > what would you expect $a to contain?
    >
    > it seems you expect [qw/this list that list/].
    >
    > I prefer, in perls above 5.6, to use subroutine prototypes:
    >
    > sub pushScalar(\@$) {
    > my $ref = shift;
    > push @$ref, shift;
    > }


    called pushScalar(@array, scalar)

    --

    "Six by nine. Forty two."
    "That's it. That's all there is."
    "I always thought something was fundamentally wrong with the universe"
    sreservoir, Mar 7, 2010
    #6
  7. Thanks for all the replies. Upon further testing, I see that
    dereferencing works the same way in "C" language functions.

    Retrieving $arrayRef = shift, and then referring to @$arrayRef, to
    avoid making a copy, works as expected, is efficient, and is readable.

    Jerry
    Jerry Krinock, Mar 8, 2010
    #7
  8. Jerry Krinock

    Uri Guttman Guest

    >>>>> "JK" == Jerry Krinock <> writes:

    JK> Thanks for all the replies. Upon further testing, I see that
    JK> dereferencing works the same way in "C" language functions.

    no it doesn't. there are many difference between perl's references and
    c's pointers. if you don't learn the differences you will not appreciate
    the power of references. refs can't be modified (c's pointers can be) so
    they are always safe - no core dumps are possible via normal ref
    use. refs can only be dereferenced into their original type. c's
    pointers can be coerced to anything and abused. user code can't create a
    ref to whatever. c's pointers can be assigned to point to anywhere.

    JK> Retrieving $arrayRef = shift, and then referring to @$arrayRef, to
    JK> avoid making a copy, works as expected, is efficient, and is readable.

    and it took you this long to learn this? as i said, you picked the wrong
    source to learn perl (hell, pretty much no web source is good for
    learning perl). the perl doc perlreftut would have taught you this and
    more in less time and with less pain. then you could move on to perlre
    (the reference reference doc), perllol and perldsc. no web needed as
    these all come with your perl installation.

    uri

    --
    Uri Guttman ------ -------- http://www.sysarch.com --
    ----- Perl Code Review , Architecture, Development, Training, Support ------
    --------- Gourmet Hot Cocoa Mix ---- http://bestfriendscocoa.com ---------
    Uri Guttman, Mar 8, 2010
    #8
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Mr. SweatyFinger
    Replies:
    2
    Views:
    1,805
    Smokey Grindel
    Dec 2, 2006
  2. Gaijinco
    Replies:
    4
    Views:
    263
    Eric Jensen
    Jul 3, 2006
  3. Ben
    Replies:
    2
    Views:
    877
  4. Lawrence D'Oliveiro

    Death To Sub-Sub-Sub-Directories!

    Lawrence D'Oliveiro, May 5, 2011, in forum: Java
    Replies:
    92
    Views:
    2,015
    Lawrence D'Oliveiro
    May 20, 2011
  5. weston
    Replies:
    1
    Views:
    247
    Richard Cornford
    Sep 22, 2006
Loading...

Share This Page