Thanks Dave.
from "Perl Cookbook" (first edition):
The scalars in @_ are implicit aliases for the ones passed in, not copies. That means changing the elements of @_ in a subroutine changes the values in the subroutine's caller. This is a holdover from before Perl had proper references.
So, we can write functions that leave their arguments intact, by copying the arguments to private variables like this:
@nums = (1.4, 3.5, 6.7);
@ints = int_all(@nums); # @nums unchanged
sub int_all {
my @retlist = @_; # make safe copy for return
for my $n (@retlist) { $n = int($n) }
return @retlist;
}
We can also write functions that change their caller's variables:
@nums = (1.4, 3.5, 6.7);
trunc_em(@nums); # @nums now (1,3,6)
sub trunc_em {
for (@_) { $_ = int($_) } # truncate each argument
}
That's absolutely right. And nothing has changed there since the first edition of the cookbook.
However. Whilst this is easy to deal with when your argument is a list of scalars or an array, it's not as easy if it's a hash. Look at this...
my %hash = (1 => 100, 2 => 200, 3 => 300);
Now here we've initialised a hash from a list. That's, of course, the standard way to create a hash in Perl.
But what happens when we flatten that hash back into a list in order to print it out.
On my computer, that prints out:
1 100 3 300 2 200
On you computer it could well print something different as you can't guarantee the order that a hash is stored in.
When you pass that hash into your subroutine, the hash is flattened into a list which is then stored in the array @_.
So there you are in your subroutine with a "flattened" hash in @_. You know that alternate elements of @_ contain the keys an values from the hash, but there is no way to know what order they are in. Which makes it really hard to alter specific values in the hash.
My first demo function (double) demostrated this approach. As you can't know which order the hash is in, you can't alter specific values, but you _can_ alter all of the values by iterating across the whole of the array. I have to admit that I was slightly surprised that only the values were updated and not the keys. I need to investigate why that works like that.
And then I had an idea. We can't know what order the keys are in when stored in @_, but that doesn't matter if we copy the data into another hash. But, of course, when we copy the data into a hash and change that hash, then we're no longer updating @_ and we're back to the situation where we loose the changes once we leave the subroutine. But (I found myself thinking) what happens if we copy the altered hash back into @_ at the end of the subroutine.
So I tried it (see "double2"). And it seems to work. But I can't ever recall seeing anyone else recommend this method. So I suspect there may be a good reason for not using it. I'll investigate further, but in the meantime I can't reall recommend you using it.
You should probably just stick with either returning the altered hash from the subroutine or passing a reference in as I discussed earlier.
I'm not sure I've been particularly clear here. I'm still trying to work this stuff out in my own head.
--
Dave Cross, Perl Hacker, Trainer and Writer
http://www.dave.org.uk/ Get more help at
Perl Monks