Re: Sub hash param by reference
by stevieb (Canon) on Jul 07, 2016 at 18:44 UTC
|
I've cleaned up the code a bit, and then made some fixes. you're trying to dereference things that aren't refs ;) Read the inline comments...
use warnings;
use strict;
my %hash_rec;
$hash_rec{id} = "001";
doSub1(\%hash_rec);
print "back main, id=$hash_rec{'id'}, id2=$hash_rec{'id2'}\n";
sub doSub1 {
my ($hash_ref) = @_;
print "inside doSub1, id=$hash_ref->{id}\n";
# you're not assigning a reference to $hash_ref->{id2},
# you're assigning a simple string
$hash_ref->{id2} = "002";
# you're only sending a scalar string to doSub2()...
# $hash_ref->{id2} eq "002"
doSub2($hash_ref->{id2});
}
sub doSub2 {
# $id2_ref is NOT a reference... it's the value of
# $hash_ref->{id2}, which is a scalar string only
my ($id2_ref) = @_;
print "inside doSub2, id2=$id2_ref\n"; # no need to deref this!
}
Output:
inside doSub1, id=001
inside doSub2, id2=002
back main, id=001, id2=002
-> is the canonical dereference operator that you should be using, and don't put & at the beginning of sub calls. That's legacy perl 4 code, and isn't needed/shouldn't be used unless you know why you need them. Also, there's no need to define a main() sub. All portions of your program that are not wrapped in subs (or external modules, which you're not using here) already belong to "main". | [reply] [d/l] [select] |
|
|
Hi Stevie, thanks for the feedback. Your code is good, However, on doSub2 is not what I am looking for. The value pass to doSub2 need to be in reference as the value may change inside that sub.
| [reply] |
|
|
use warnings;
use strict;
my %hash_rec = (id => "001");
doSub2($hash_rec{id2});
print "main: id2 = $hash_rec{id2}\n";
sub doSub2 {
$_[0] = "Hello alias";
}
Prints:
main: id2 = Hello alias
We don't usually take advantage of that trick because without due care it's likely to lead to hard to maintain code.
Premature optimization is the root of all job security
| [reply] [d/l] [select] |
|
|
|
|
|
Re: Sub hash param by reference
by Marshall (Canon) on Jul 07, 2016 at 21:07 UTC
|
If you are trying to pass a reference to some value in a hash to a sub, that is possible. That way the sub wouldn't know the key name. More usual would be to pass a ref to the whole hash and the sub does know the key name. Here is a demo of both possibilities. Hope that I'm not confusing the issue.
#!/usr/bin/perl
use strict;
use warnings;
use Data::Dump qw(pp);
$!=1;
my %hash;
$hash{id1} = 001;
$hash{id2} = 002;
print "initial hash: ", pp (\%hash), "\n";
my $hash_ref = \%hash;
my $ref = \$hash{id2}; # A reference to id2
$$ref = 003; #use ref to a scalar (value of id2)
print "modified hash: ", pp (\%hash), "\n";
modify_anon_hash_value ($ref);
print "mod by sub : ", pp (\%hash), "\n";
more_normal_way($hash_ref);
print "mod more usual: ", pp ($hash_ref), "\n";
sub modify_anon_hash_value #sub doesn't know which
{ #key's value that it is
my $ref = shift; #modifying
$$ref = 5;
}
sub more_normal_way #usual pass the entire hash as ref
{
my $href = shift;
$href->{id2}=8; #sub know which key to use
}
__END__
initial hash: { id1 => 1, id2 => 2 }
modified hash: { id1 => 1, id2 => 3 }
mod by sub : { id1 => 1, id2 => 5 }
mod more usual: { id1 => 1, id2 => 8 }
| [reply] [d/l] |
|
|
Thank you Marshall, my $ref = \$hash{id2} is exactly what I am trying to look for.
Your given example is perfect. I'm just curious, when calling sub modify_anon_hash_value ($ref) is it possible to do it like modify_anon_hash_value (\$hash{id2}) ? without need to do my $ref = \$hash{id2}.
I have test run it with
modify_anon_hash_value (\$hash{id2});
The result return correctly without any error. Just to confirm if this way of coding is allowed or it may be a bad way to do it. | [reply] [d/l] |
|
|
Yes, you will be fine without creating an intermediate scalar. Using this method (modify_anon_hash_value (\$hash{id2});), I see right away in the callers code that a modification to $hash{id2} is what is intended.
What is sent to the sub are aliases. It is possible to "hide" this reference creation in the sub. For fun, I coded the routine "confusing" below. The caller wouldn't expect that to happen, so I wouldn't do that.
#!/usr/bin/perl
use strict;
use warnings;
use Data::Dump qw(pp);
$!=1;
my %hash;
$hash{id1} = 001;
$hash{id2} = 002;
print "initial hash: ", pp (\%hash), "\n";
my $hash_ref = \%hash;
modify_anon_hash_value (\$hash{id2});
print "mod by sub : ", pp (\%hash), "\n";
confusing ($hash{id2});
print "mod by confusing : ", pp (\%hash), "\n";
sub modify_anon_hash_value #sub doesn't know which
{ #key's value that it is
my $ref = shift; #modifying
$$ref = 5;
}
sub confusing #a confusing mess
{
my $ref = \(shift); #same as $ref = \$_[0]
$$ref=9; # @_ has aliases, not a copy
}
__END__
initial hash: { id1 => 1, id2 => 2 }
mod by sub : { id1 => 1, id2 => 5 }
mod by confusing : { id1 => 1, id2 => 9 }
| [reply] [d/l] [select] |
|
|
|
|
Hi Marshall, sorry I have confused your previous answer with my initial questions. Your previous answer is helpful. Just my initial case is little bit different. When inside a sub, the value pass in is already a hash reference and I would like to call another sub but only in refer to specific rec of that hash. Please have a look of the codes below;
my (%hash);
$hash{'id1'} = "1";
doSub1(\%hash);
# the return id2 should be changed within doSub2 when doSub1 call it.
print "$hash{'id2'}\n";
doSub1 {
# accept param as hash by reference
my ($hrec_ref) = @_;
$$hrec_ref{'id2'} = "0";
# Going to call doSub2 by passing Only $$hrec_ref{'id2'} as reference.
# Because $hrec_ref is a hash reference already, How I can call it?
# doSub2(\$hrec_ref{'id2'}); Return Error. Not valid.
# doSub2($hrec_ref{'id2'}); Also return Error.
}
doSub2 {
# handle only a single string param by reference
my ($href) = @_;
$$href = "value changed in doSub2";
}
My problem confusing now is IF the hash is in a reference value and I need call another sub to process only specific hash rec. My current only way to deal with this is;
#... inside doSub1
my ($s) = $$hrec_ref{'id2'};
doSub2(\$s);
$$hrec_ref{'id2'} = $s;
I'm trying to see if there are any shortcut version of coding it. | [reply] [d/l] [select] |
|
|
use strict;
use warnings;
my (%hash);
$hash{'id1'} = "1";
doSub1 (\%hash);
# the return id2 should be changed within doSub2 when doSub1 call it.
print "$hash{'id2'}\n";
sub doSub1 {
# accept param as hash by reference
my ($hrec_ref) = @_;
$$hrec_ref{'id2'} = "0";
# doSub2(\$hrec_ref{'id2'}); Return Error. Not valid. (works for me -
+Hippo)
doSub2 (\$hrec_ref->{'id2'});
}
sub doSub2 {
# handle only a single string param by reference
my ($href) = @_;
$$href = "value changed in doSub2";
}
| [reply] [d/l] |
|
|
Re: Sub hash param by reference
by perlfan (Parson) on Jul 07, 2016 at 20:21 UTC
|
You can pass the whole hash by reference, then deref what you want using a hash slice if that's what you mean. Otherwise, what's the point if it's all by reference?
(tested on 5.22.1) See perldata for more info.
use strict;
use warnings;
use Data::Dumper;
sub slice_by_ref {
my $h = shift;
my %subset = %$h{'foo', 'bar'};
print Data::Dumper::Dumper(\%subset);
}
my $h = {blonk => 2, foo => 3, squink => 5, bar => 8};
slice_by_ref($h);
OUTPUT:
$VAR1 = {
'foo' => 3,
'bar' => 8
};
| [reply] [d/l] [select] |
Re: Sub hash param by reference
by TomDLux (Vicar) on Jul 11, 2016 at 19:58 UTC
|
So long as your subset is a nested hashref or nested array, this works nicely. I've done something similar to spread processing across dedicated routines.
my $results = process_planet( \%world );
sub process_planet {
my ( $world ) = @_;
my @retval;
for my $country_id ( keys %$world ) {
my $population = process_country( $world->{$country_id} )
+;
push @retval, { $country_id => $population };
}
return \@retval;
}
sub process_country {
my ( $country ) = @_;
my $national_pop;
for my $city ( keys %$country ) {
$national_pop += extract_city_population( $countr->{$city}
+ );
}
return $national_pop;
}
Of course, at eavh level you have to know what you're receiving, and what you're returning.
As Occam said: Entia non sunt multiplicanda praeter necessitatem.
| [reply] [d/l] |