![]() |
Why does assigning @_ cause sub's parameter to be copied?
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/codec...rl/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 |
Re: Why does assigning @_ cause sub's parameter to be copied?
Jerry Krinock <jerry@ieee.org> 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 |
Re: Why does assigning @_ cause sub's parameter to be copied?
>>>>> "JK" == Jerry Krinock <jerry@ieee.org> writes:
JK> 1. http://www.troubleshooters.com/codec...rl/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 ------ uri@stemsystems.com -------- http://www.sysarch.com -- ----- Perl Code Review , Architecture, Development, Training, Support ------ --------- Gourmet Hot Cocoa Mix ---- http://bestfriendscocoa.com --------- |
Re: Why does assigning @_ cause sub's parameter to be copied?
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/codec...rl/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 |
Re: Why does assigning @_ cause sub's parameter to be copied?
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" |
Re: Why does assigning @_ cause sub's parameter to be copied?
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" |
Re: Why does assigning @_ cause sub's parameter to be copied?
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 |
Re: Why does assigning @_ cause sub's parameter to be copied?
>>>>> "JK" == Jerry Krinock <jerry@ieee.org> 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 ------ uri@stemsystems.com -------- http://www.sysarch.com -- ----- Perl Code Review , Architecture, Development, Training, Support ------ --------- Gourmet Hot Cocoa Mix ---- http://bestfriendscocoa.com --------- |
| All times are GMT. The time now is 12:16 AM. |
Powered by vBulletin®. Copyright ©2000 - 2013, vBulletin Solutions, Inc.
SEO by vBSEO ©2010, Crawlability, Inc.