Re: Global variable unexpectedly modified when passed by reference
by GotToBTru (Prior) on Dec 08, 2014 at 14:21 UTC
|
The usual reason to pass a variable by reference is to allow changes in the subroutine! Pass by reference means the subroutine accesses the very same memory location as the main program. If you want the variable to remain intact, pass it by value.
use Data::Dumper;
my %hash = (a => 1, b => 2);
mysub(\%hash);
print Dumper(\%hash);
my %hash2 = (a => 1, b => 2);
mysub1(%hash2);
print Dumper(\%hash2);
sub mysub{
my $h = shift;
delete $h->{'b'};
}
sub mysub1{
my %h = @_;
delete $h{'b'};
}
$VAR1 = {
'a' => 1
};
$VAR1 = {
'a' => 1,
'b' => 2
};
Updated to include code example. Updated again to fix error.
| [reply] [d/l] [select] |
|
|
my %h = shift;
in mysub1 is not doing what you think.
$VAR1 = {
'a' => 1
};
Odd number of elements in hash assignment at ./spw1109575_GTBT line 21
+.
$VAR1 = {
'b' => 2,
'a' => 1
};
The shift is taking the first element from the @_ array in which the %hash2 hash was passed to the subroutine and assigning that single scalar to the %h hash in the subroutine, hence the "Odd number" warning. You got the correct result from the Dump output anyway because passing by value, even the incorrect one, doesn't affect the original which is what you were illustrating.
What you should have done is assign all of @_ to the hash
use strict;
use warnings;
use Data::Dumper;
my %hash = (a => 1, b => 2);
mysub(\%hash);
print Dumper(\%hash);
my %hash2 = (a => 1, b => 2);
mysub1(%hash2);
print Dumper(\%hash2);
sub mysub{
my $h = shift;
delete $h->{'b'};
}
sub mysub1{
my %h = @_;
delete $h{'b'};
}
which produces
$VAR1 = {
'a' => 1
};
$VAR1 = {
'a' => 1,
'b' => 2
};
I hope this is of interest.
| [reply] [d/l] [select] |
|
|
| [reply] [d/l] |
|
|
Thanks for the clarification about the way the reference is used. The code I supplied is an example and in reality %hash would be the result of another sub call, so I can't pass by value. As I need to then call other subs with \%hash as an argument (some of which need to modify %hash), I'll need to find another way.
| [reply] |
|
|
#!/perl
use warnings; use strict; use 5.010;
my $got = throw_ref();
my %hash = change_hash( %{ $got } );
say 'original hash now ...';
show_hash( %{ $got } );
exit;
sub change_hash
{
my ( %h ) = @_;
say 'before modification ...';
show_hash( %h );
$h{'p'} += 10;
$h{'q'} = 3;
say 'after modification ...';
show_hash( %h );
return %h;
}
sub show_hash
{
my ( %h ) = @_;
printf "%s : %s\n" , $_ , $h{ $_ } for sort keys %h;
return;
}
sub throw_ref
{
return { 'p' => -2 } ;
}
__END__
before modification ...
p : -2
after modification ...
p : 8
q : 3
original hash now ...
p : -2
... But if the hash reference is big and/or tied, then dereference could be expensive and/or undesired. | [reply] [d/l] |
|
|
| [reply] |
|
|
sub mysub{
my $h = shift;
my %dereferenced_hash = %$h;
delete $dereferenced_hash{'b'};
}
| [reply] [d/l] |
Re: Global variable unexpectedly modified when passed by reference
by AnomalousMonk (Archbishop) on Dec 08, 2014 at 17:51 UTC
|
A note of caution. Copying by dereference as shown in examples above is shallow copying only. In a nested data structure (e.g., hash-of-hashes, array-of-arrays, etc.), a reference may be present at any level. (Indeed, in Perl, a nested data structure must, by definition, contain one or more references at one or more levels. See perldsc.) Had the hashes used in the examples above contained a hash/array/whatever reference as a value, the problem would potentially have just been moved down one or more levels. A reference remains a reference to the original referent no matter how many times it is simply copied.
c:\@Work\Perl\monks>perl -wMstrict -MData::Dump -le
"my @ra = (1, 2, 3);
my %hash = ( a => 'ok', b => \@ra, );
;;
S(\%hash);
;;
dd \@ra;
dd \%hash;
;;
sub S {
my $hashref = shift;
my %hashcopy = %$hashref;
;;
return do_something_with(\%hashcopy);
}
;;
sub do_something_with {
my $hashref = shift;
;;
$hashref->{b}[0] = 9;
$hashref->{b}[1] = 9;
$hashref->{b}[2] = 9;
return 1;
}
"
[9, 9, 9]
{ a => "ok", b => [9, 9, 9] }
| [reply] [d/l] |
Re: Global variable unexpectedly modified when passed by reference
by Ratazong (Monsignor) on Dec 08, 2014 at 14:30 UTC
|
Hi
It seems you are mixing call by reference and call by value. You might want to check the descriptions of both, e.g. here in wikipedia (subchapters 1.2 and 1.3)
HTH, Rata
| [reply] |
|
|
Sorry I already wanted to nitpick when GotToBTru mentioned this terminology. :)
Technically all calls in Perl are call-by-reference , cause @_ holds so called aliases (implicitly)
But copying from @_ makes most Perl routines de facto act like call-by-value .
So what the OP does is a simulated call by value where the value is an explicit reference.
I feel the need to clarify this cause the terminology is older than Perl and meant to describe languages without explicit ref type. ;)
These concepts are meant to describe differences between languages not Perl features.
For instance JS has a mix of both:
- simple types ("scalars") are always passed by value
- objects (including arrays and "hashes") always as references.
edit
Should be noted that JS doesn't have an explicit ref operator.
Concepts in "quotes" only translate roughly and would need deeper explanation.
Cheers Rolf
(addicted to the Perl Programming Language and ☆☆☆☆ :)
| [reply] [d/l] [select] |
Re: Global variable unexpectedly modified when passed by reference
by wee (Scribe) on Dec 10, 2014 at 23:00 UTC
|
The global %hash is being altered because you are making alterations to its reference. What you want to do is dereference it in mysub(), like so:
#!/usr/bin/perl
use strict;
use warnings;
use Data::Dumper;
my %hash = (a => 1, b => 2);
mysub(\%hash);
print Dumper(\%hash);
sub mysub {
my $h = shift;
my %new_hash = %$h;
delete($new_hash{'b'});
}
You can do whatever you want to %new_hash and nothing will happen to %hash. | [reply] [d/l] |
Re: Global variable unexpectedly modified when passed by reference
by locked_user sundialsvc4 (Abbot) on Dec 08, 2014 at 17:21 UTC
|
For those of you who might be schooled in other programming languages, it might be useful to you for me to point out that Perl really doesn’t have the notion of “[passing parameters by] reference.” Instead, it has a generally-applicable notion of: references.
“A reference” is, first of all, sort-of “a data type.” In other words, “any variable’s value” could be (say ...) integer, string, floating-point, file-handle, or ... “reference.” Every “thing” in the Perl world has a “reference count” associated with it, and the Perl runtime system somehow keeps everything straight, so that nothing gets de-allocated too soon.
Nevertheless, this creates a situation that catches many programmers who are accustomed to “strongly-typed,” “compiler-oriented” language systems somewhat off-guard: it’s all very dynamic. When a variable contains “a reference,” anything that you do to it is automatically and transparently conveyed to act upon its “target.” But, that very same variable could at some time cease to contain a “reference” value and, instead, contain an ordinary scalar value, in which case it would behave altogether differently than before. The runtime behavior of the variable is not bound-to nor determined-by its compile-time (syntactic ...) characteristics. Instead, that behavior is determined by its (perhaps, very immediate) history and provenance.
“With great power comes great responsibility ...”
| |
|
|
When a variable contains "a reference," anything that you do to it is automatically and transparently conveyed to act upon its "target." But, that very same variable could at some time cease to contain a "reference" value and, instead, contain an ordinary scalar value, in which case it would behave altogether differently than before.
From perlref:
References are easy to use in Perl. There is just one overriding principle: in general, Perl does no implicit referencing or dereferencing. When a scalar is holding a reference, it always behaves as a simple scalar. It doesn't magically start being an array or hash or subroutine; you have to tell it explicitly to do so, by dereferencing it.
You are once again completely, utterly wrong. Dereferencing is a general concept that applies to many programming languages.
Your posting history has revealed your business model:
Step 1: Market yourself as someone who fixes "legacy software projects that have wandered far off-course", i.e. cleaning up others' messes
Step 2: Troll programming websites and (a) spread misinformation to those seeking help and (b) cause other professional Perl devs to waste their time proofreading and correcting you instead of helping people
Step 3: Profit!
Your LinkedIn profile claims you were an instructor at Scottsdale Community College. I can only assume you used the same approach there, training the next generation of bad programmers. Well, I guess us average Perl devs might be thankful to you for screwing the curve and making us look better. Or maybe you already recognized that and are getting paid for this service by other developers - that'd be quite a racket!
| [reply] |
|
|
Fellow monks, as the author of the parent node* I kindly request you approve its reaping.
Most of it is obviously just a rant which I no longer think needs to remain on permanent record
(although I do hope it will remain in sundialsvc4's mind as a motivation to post better content).
I've re-posted the purely technical content of the node below, I think it communicates all that needs to be said.
* If there are doubts, perhaps one of the gods could confirm using the server logs.
| [reply] |
A reply falls below the community's threshold of quality. You may see it by logging in.
|
|
|
| [reply] [d/l] |
|
|
"With great power comes great responsibility ..."
Yet you take no responsibility for spamming this site with incorrect information. Someone seems to have figured out your motives for continually doing so. The fact that, at time of writing, this node has positive reputation (+1) is embarrassing.
| [reply] |
|
|
$ perl -E'sub { $_[0] = "abc" }->($x); say $x'
abc
I think you wanted to point out the phrase "passing by reference" has been incorrectly used instead of "passing a reference" throughout this thread, but you took a wrong turn somewhere.
| [reply] [d/l] |
|
|
| [reply] |
| A reply falls below the community's threshold of quality. You may see it by logging in. |