Passing a hash by reference

N

Niall Macpherson

Apologies if this has been covered before - I have seen a number of
posts on this subject but I still cannot work out my code doesn't
work.

I have been working with C / C++ for over 10 years and am fully
conversant with pointers and passing by reference in C. However ,
passing by reference in perl is causing me some problems.

Here is a little bit of test code -

##----------------------------------------------------------------------
sub AddHashEntries
{
my($rh_hashref) = @_;
for($cnt = 0; $cnt < 10; $cnt ++)
{
$value = "value " . $cnt;
$key = "key" . $cnt;
$$rh_hashref{$key} = $value;
}
return;
}
##----------------------------------------------------------------------
sub PrintHashEntries
{
my($rh_hashref) = @_;

foreach my $key (keys $$rh_hashref)
{
print("\nKey = [$key], value = $$rh_hashref{$key}");
}
return;
}
##----------------------------------------------------------------------
my %testhash = {};
AddHashEntries(\%testhash);
print("\nAdded all entries OK");
foreach my $lkey (keys %testhash) ## *** 1 ****
{
print("\nKey = [$lkey], value = $testhash{$lkey}");
}
PrintHashEntries(\%testhash);
##----------------------------------------------------------------------


The values appear to get added to hash OK - the call at *** 1 ****
produces output as I would expect , eg

Added all entries OK
Key = [key7], value = value 7
Key = [HASH(0x1abefac)], value =
Key = [key8], value = value 8
Key = [key9], value = value 9
Key = [key0], value = value 0
Key = [key1], value = value 1
Key = [key2], value = value 2
Key = [key3], value = value 3
Key = [key4], value = value 4
Key = [key5], value = value 5
Key = [key6], value = value 6

However , I can't even get the PrintHashEntries routine to compile - I
get

Type of arg 1 to keys must be hash (not scalar dereference) at
C:\MoreTestStuff\
GMPriceConf\hashreftest.pl line 20, near "$rh_hashref)
"
Execution of C:\MoreTestStuff\GMPriceConf\hashreftest.pl aborted due
to compilat
ion errors.


Can anyone tell me what I am doing wrong ?

Thanks
 
T

Tore Aursand

sub AddHashEntries
{
my($rh_hashref) = @_;
for($cnt = 0; $cnt < 10; $cnt ++)
{
$value = "value " . $cnt;
$key = "key" . $cnt;
$$rh_hashref{$key} = $value;
}
return;
}

sub AddHashEntries {
my $rh_hashref = shift;

for ( 1..10 ) {
my $value = 'value ' . $_;
my $key = 'key' . $_;
$rh_hashref->{$key} = $value;
}
}
sub PrintHashEntries
{
my($rh_hashref) = @_;

foreach my $key (keys $$rh_hashref)
{
print("\nKey = [$key], value = $$rh_hashref{$key}");
}
return;
}

sub PrintHashEntries {
my $rh_hashref = shift;

foreach ( keys %$rh_hashref ) {
print "\nKey = [$key], value = $rh_hashref->{$_}";
}
}
 
U

Uri Guttman

TA" == Tore Aursand said:
sub AddHashEntries {
my $rh_hashref = shift;
for ( 1..10 ) {
my $value = 'value ' . $_;
my $key = 'key' . $_;
$rh_hashref->{$key} = $value;
}
}

$rh_hashref->{"key$_"} = "value$_" for 1 .. 10 ;

uri
 
B

Bob Walton

Niall said:
Apologies if this has been covered before - I have seen a number of
posts on this subject but I still cannot work out my code doesn't
work.

I have been working with C / C++ for over 10 years and am fully
conversant with pointers and passing by reference in C. However ,
passing by reference in perl is causing me some problems.

Here is a little bit of test code -

##----------------------------------------------------------------------


use strict;
use warnings;

If you had used warnings, Perl would have told you what was wrong. Let
Perl help you.

sub AddHashEntries
{
my($rh_hashref) = @_;
for($cnt = 0; $cnt < 10; $cnt ++)
{
$value = "value " . $cnt;
$key = "key" . $cnt;
$$rh_hashref{$key} = $value;
}
return;
}
##----------------------------------------------------------------------
sub PrintHashEntries
{
my($rh_hashref) = @_;

foreach my $key (keys $$rh_hashref)

%-----------------------------^
That won't compile. Keys expects a hash, not a scalar.

{
print("\nKey = [$key], value = $$rh_hashref{$key}");
}
return;
}
##----------------------------------------------------------------------
my %testhash = {};

()---------------^^
Using {} generates a scalar value which is a reference to an anonymous
hash. That is the "weird" value that is showing up in your printed hash
contents. This hash reference goes in as a key; keys must be strings,
so it is stringified, and this stringified hash reference is the
HASH(0x1abefac) key in your output. The value is the empty string
because %testhash = expects to find a list with an even number of
entries; if an odd number (like one) is present, Perl emits a warning
(if you asked for warnings as you should during development) and assigns
undef as the value.

AddHashEntries(\%testhash);
print("\nAdded all entries OK");
foreach my $lkey (keys %testhash) ## *** 1 ****
{
print("\nKey = [$lkey], value = $testhash{$lkey}");
}
PrintHashEntries(\%testhash);
##----------------------------------------------------------------------


The values appear to get added to hash OK - the call at *** 1 ****
produces output as I would expect , eg

Added all entries OK
Key = [key7], value = value 7
Key = [HASH(0x1abefac)], value =
Key = [key8], value = value 8
Key = [key9], value = value 9
Key = [key0], value = value 0
Key = [key1], value = value 1
Key = [key2], value = value 2
Key = [key3], value = value 3
Key = [key4], value = value 4
Key = [key5], value = value 5
Key = [key6], value = value 6

However , I can't even get the PrintHashEntries routine to compile - I
get

Type of arg 1 to keys must be hash (not scalar dereference) at
C:\MoreTestStuff\
GMPriceConf\hashreftest.pl line 20, near "$rh_hashref)
"
Execution of C:\MoreTestStuff\GMPriceConf\hashreftest.pl aborted due
to compilat
ion errors.
....
 
E

Eric J. Roode

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

(e-mail address removed) (Niall Macpherson) wrote in
foreach my $key (keys $$rh_hashref)

In C/C++, * is used for all dereferencing. In Perl, you can
dereference with $, @, or %, depending on what sort of thing you're
dereferencing.

The 'keys' operator expects to work on a real hash, not a reference
or anything, so you need to dereference it with %. The next
nonwhitespace character after the word keys *must* be a %.
(Similarly, the character following 'each' must be a %; the character
following 'push' must be @).

What you want is: keys %$rh_hashref.

- --
Eric
$_ = reverse sort $ /. r , qw p ekca lre uJ reh
ts p , map $ _. $ " , qw e p h tona e and print
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.2.1 (MingW32) - WinPT 0.5.13

iD8DBQE/2u6XY96i4h5M0egRAkBXAKCbY1Jbxhdr7FKe5nm0JlNFHy7IcACfVemR
+bSgHZI/sdM89CV/XEAL6/E=
=qSNr
-----END PGP SIGNATURE-----
 
W

Web Surfer

[This followup was posted to comp.lang.perl.misc]

Apologies if this has been covered before - I have seen a number of
posts on this subject but I still cannot work out my code doesn't
work.

I have been working with C / C++ for over 10 years and am fully
conversant with pointers and passing by reference in C. However ,
passing by reference in perl is causing me some problems.

Here is a little bit of test code -

##----------------------------------------------------------------------
sub AddHashEntries
{
my($rh_hashref) = @_;
for($cnt = 0; $cnt < 10; $cnt ++)
{
$value = "value " . $cnt;
$key = "key" . $cnt;
$$rh_hashref{$key} = $value;
}
return;
}
##----------------------------------------------------------------------
sub PrintHashEntries
{
my($rh_hashref) = @_;

foreach my $key (keys $$rh_hashref)

This should be

foreach my $key ( keys %$rh_hashref )

{
print("\nKey = [$key], value = $$rh_hashref{$key}");
}
return;
}
##----------------------------------------------------------------------
my %testhash = {};
AddHashEntries(\%testhash);
print("\nAdded all entries OK");
foreach my $lkey (keys %testhash) ## *** 1 ****
{
print("\nKey = [$lkey], value = $testhash{$lkey}");
}
PrintHashEntries(\%testhash);
##----------------------------------------------------------------------


The values appear to get added to hash OK - the call at *** 1 ****
produces output as I would expect , eg

Added all entries OK
Key = [key7], value = value 7
 
G

G Klinedinst

TORE> sub PrintHashEntries {
TORE> my $rh_hashref = shift;
TORE>
TORE> foreach ( keys %$rh_hashref ) {
TORE> print "\nKey = [$key], value = $rh_hashref->{$_}";
TORE> }
TORE> }

TORE> sub AddHashEntries {
TORE> my $rh_hashref = shift;
TORE>
TORE> for ( 1..10 ) {
TORE> my $value = 'value ' . $_;
TORE> my $key = 'key' . $_;
TORE> $rh_hashref->{$key} = $value;
TORE> }
TORE> }

URI> $rh_hashref->{"key$_"} = "value$_" for 1 .. 10 ;

Thank you Uri and Tore for "Perlizing" his code. I also write Perl
code which looks suspiciously like C, so seeing how everyone else does
it is a great help to me. I particularly like the "1..10" and shifting
the first value out of @_ at the beginning of each function...I mean
subroutine.

-Greg
 
T

Toby

TORE> sub PrintHashEntries {
TORE> my $rh_hashref = shift;
TORE>
TORE> foreach ( keys %$rh_hashref ) {
TORE> print "\nKey = [$key], value = $rh_hashref->{$_}";

surely:
print "\nKey = [$_], value = $rh_hashref->{$_}";
 
U

Uri Guttman

TORE> sub PrintHashEntries {
TORE> my $rh_hashref = shift;

TORE> sub AddHashEntries {
TORE> my $rh_hashref = shift;

URI> $rh_hashref->{"key$_"} = "value$_" for 1 .. 10 ;
Thank you Uri and Tore for "Perlizing" his code. I also write Perl
code which looks suspiciously like C, so seeing how everyone else does
it is a great help to me. I particularly like the "1..10" and shifting
the first value out of @_ at the beginning of each function...I mean
subroutine.

well, since you are in a learning mode, i prefer to assign with @_
instead of shift (except where shifting @_ does something important).

my ( $arg, $foo ) = @_ ;

that style will usually mean fewer lines of code and it is easier to add
more args or slurp the rest with an array or hash:

my ( $file, %opts ) = @_ ;

but as i said shift has its place there but IMO only when you need to
have a shorter @_ for use later on.

uri
 
A

Anno Siegel

Uri Guttman said:
TORE> sub PrintHashEntries {
TORE> my $rh_hashref = shift;

TORE> sub AddHashEntries {
TORE> my $rh_hashref = shift;

URI> $rh_hashref->{"key$_"} = "value$_" for 1 .. 10 ;


well, since you are in a learning mode, i prefer to assign with @_
instead of shift (except where shifting @_ does something important).

my ( $arg, $foo ) = @_ ;

that style will usually mean fewer lines of code and it is easier to add
more args or slurp the rest with an array or hash:

my ( $file, %opts ) = @_ ;

but as i said shift has its place there but IMO only when you need to
have a shorter @_ for use later on.

One such place is, in my praxis, the first parameter of a method. It
is a good rule to always shift it off the parameter list (and probably
call it $class for a class method, and $self, or an abbreviation of the
class name, for an object method). That way, the parameters left after
the shift correspond to the (method-) call parameters the way they do
with normal sub calls. It also gives methods a distinctive style that
may help tell them from other subs.

Anno
 

Ask a Question

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

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,755
Messages
2,569,536
Members
45,007
Latest member
obedient dusk

Latest Threads

Top