I am passing an array of references by reference to a subroutine. When I try to deference it in a sub-routine, it gives a flattened hash. How can I fix this? I don't want to have a flat hash and I am unable determine the reason for this.
I am sure that I am making a mistake somewhere but not able to spot it. Any comments/suggestions are totally welcome! Looking forward to hear from this wonderful community! Thanks in advance.
updated problem statement:
Basically I am looking to pass a hash by reference to a sub-routine. And my issue is that when I accept it in the subroutine with a scalar variable, and then I try to de-reference it with % symbol, I still get a flat hash.
update: There was a confusion.As I was checking whether my hash is flat or not - I checked only with print Dumper %hash when I should actually have actually checked with print Dumper \%hash. Lack of this piece of information caused this issue.
Script:
#!/usr/bin/perl
use strict ;
use warnings ;
use Data::Dumper ;
my %h = (moe => "joe", toe => "poe") ;
my #a = (1,2,3,4) ;
my #refs = \(%h,#a) ;
sub sample()
{
my $ref = shift ;
my #refs = #{$ref} ;
print "What I got in the sub! Seems OK!!\n" ;
print Dumper #refs, "\n" ;
my %h = %{$refs[0]} ;
my #a = #{$refs[1]} ;
print "I am not able to dereference this :(. Please help!! Hash is flat :(\n" ;
print Dumper %h ;
print Dumper #a ;
}
&sample(\#refs) ;
OUTPUT:
23:34:17[Host#User]$ ./test.pl
What I got in the sub! Seems OK!!
$VAR1 = {
'moe' => 'joe',
'toe' => 'poe'
};
$VAR2 = [
1,
2,
3,
4
];
$VAR3 = '
';
I am not able to dereference this :(. Please help!! Hash is flat :(
$VAR1 = 'moe';
$VAR2 = 'joe';
$VAR3 = 'toe';
$VAR4 = 'poe';
$VAR1 = 1;
$VAR2 = 2;
$VAR3 = 3;
$VAR4 = 4;
There's nothing to fix. You have what you wanted. You have a hash in %h and an array in #a.
But Data::Dumper takes a list of arguments and it treats each of its arguments as a separate variable to dump. So when you pass either a hash or an array to Dumper(), they will be unrolled into a list and you'll get them displayed as separate variables.
If you want to see the structure of an array or a hash using Dumper(), you should pass in a reference to the data structure instead.
print Dumper \%h;
print Dumper \#a;
Of course, that's effectively what you're doing on your first call to Dumper().
print Dumper #refs;
I should also point out that you have a couple of errors in your code that (fortunately?) cancel each other out. You define your subroutine sample with an empty prototype (sample() { ... }) which means that you will get a fatal error if you pass it any arguments. But when you call the subroutine, you use an & (&sample(#refs)) and one of the effects of that is to turn off prototype checking - so it works even though you pass arguments to the subroutine.
Best to omit the prototype completely (sub sample { ... }) and call the subroutine without the ampersand (sample(#refs))).
Related
I am trying to read the contents of a file into a hash
file contents look line,
A|A1
B|B1
C|C1
the code I have is
use strict;
use warnings;
use Data::Dumper;
my $instAttribFileName="DATABYIDENTIFIER_InstCommonAttrList.config";
open(IFH,$instAttribFileName) or die "cannot open file";
my %attribhash = ();
%attribhash = map {chomp; split /\|/} (<IFH>);
print Dumper %attribhash;
Dumper does not print the hash but reads A,A1 etc into seperate variables.
what am I doing wrong here?
According to perldoc perldata:
LISTs do automatic interpolation of sublists. That is, when a LIST is
evaluated, each element of the list is evaluated in list context, and
the resulting list value is interpolated into LIST just as if each
individual element were a member of LIST. Thus arrays and hashes lose
their identity in a LIST
So you need to pass the hash by reference to Dumper() or else it will be flattened into a list of separate arguments. For example, if you have:
my %foo = ( a => 'A', b => 'B');
print Dumper %foo;
Output:
$VAR1 = 'b';
$VAR2 = 'B';
$VAR3 = 'a';
$VAR4 = 'A';
but if you pass a reference to %foo instead (by putting a backslash in front):
print Dumper \%foo;
we get:
$VAR1 = {
'b' => 'B',
'a' => 'A'
};
References:
Data::Dumper
perldoc perlref
How can I print the contents of a hash in Perl?
It's always worth reading all of the documentation for the modules you're trying to use. The "BUGS" section in the Data::Dumper manual says:
Due to limitations of Perl subroutine call semantics, you cannot pass an array or hash. Prepend it with a \ to pass its reference instead.
My understanding was that in Perl we pass hashes to functions by reference
Consider the following example, where we modify the hash in the modifyHash function
#!/usr/local/bin/perl
my %hash;
$hash{"A"} = "1";
$hash{"B"} = "2";
print (keys %hash);
print "\n";
modifyHash(\%hash);
print (keys %hash);
print "\n";
sub modifyHash {
my $hashRef = #_[0];
my %myHash = %$hashRef;
$myHash{"C"} = "3";
print (keys %myHash);
print "\n";
}
The output of this script is:
AB
ABC
AB
I would have expected it to be:
AB
ABC
ABC
...as we pass the hash by reference.
What concept am I missing here about passing hashes to functions?
That's because when you do my %myHash = %$hashRef;, you're taking a copy of the dereferenced $hashref and putting it into %myHash which is the same thing as my %myHash = %hash;, so you're not working on the referenced hash at all.
To work on the hash specified by the reference, try this...
sub modifyHash {
my $hashRef = $_[0];
$hashRef->{"C"} = "3";
print (keys %$hashRef);
print "\n";
}
As pointed out by ThisSuitIsBlackNot in the comments below, #_[0] is better written as $_[0]. You should always be using use strict; and use warnings;, as this would have been caught. Because you're sending in a reference, you could also have used my $hashRef = shift;.
The problem is with the assignment:
my %myHash = %$hashRef;
This is akin to saying:
$x = 5;
$y = $x;
You're not setting $y to reference the same spot in memory, you're just giving the value of $x to $y. In your example, you're creating a new hash (%myHash) and giving it the value of the hash stored at $hashRef. Any future changes are to the new hash, not the original.
If you want to manipulate the original, you should do something like:
${$hashRef}{"C"} = "3";
or
$hashRef->{"D"} = 4;
There might be a more elegant way of doing it, but as far as I know you want to work with the hash reference.
I am having trouble understanding the hash references and changing the hash in place, instead of returning it. I want to write a sub routine which will return a value from hash and also modify the hash. I was facing some issues while coding for it. So, I wrote the following basic code to understand modifying the hash in place.
#!/usr/local/bin/perl
#Check hash and array references
#Author: Sidartha Karna
use warnings;
use strict;
use Data::Dumper;
sub checkHashRef{
my ($hashRef, $arrVal) = #_;
my %hashDeref = %{$hashRef};
$hashDeref{'check'} = 2;
push(#{$arrVal}, 3);
print "There:" ;
print Dumper $hashRef;
print Dumper %hashDeref;
print Dumper $arrVal
}
my %hashVal = ('check', 1);
my #arrVal = (1, 2);
checkHashRef(\%hashVal, \#arrVal);
print "here\n";
print Dumper %hashVal;
print Dumper #arrVal;
The output observed is:
There:$VAR1 = {
'check' => 1
};
$VAR1 = 'check';
$VAR2 = 2;
$VAR1 = [
1,
2,
3
];
here
$VAR1 = 'check';
$VAR2 = 1;
$VAR1 = 1;
$VAR2 = 2;
$VAR3 = 3;
From the output, I inferred that, changes to hashDeref are not modifying the data in the reference. Is my understanding correct? Is there a way to modify the hash variable in place instead of returning it.
This is making a (shallow) copy of %hashVal:
my %hashDeref = %{$hashRef};
The hash-ref $hashRef still points to %hashVal but %hashDeref doesn't, it is just a copy. If you want to modify the passed hash-ref in-place, then work with the passed hash-ref:
sub checkHashRef{
my ($hashRef, $arrVal) = #_;
$hashRef->{'check'} = 2;
#...
That will leave your changes in %hashVal. In the array case, you never make a copy, you just dereference it in-place:
push(#{$arrVal}, 3);
and the change to $arrVal shows up in #arrVal.
I have a problem accessing variables in a hash of hashes I don't know what I have done wrong. While debugging the value of hash %list1 gives an undef, So I can't get my values out .
use strict ;
use warnings ;
my $text = "The, big, fat, little, bastards";
my $Author = "Alex , Shuman ,Directory";
my %hashes = {1,2,3,40};
my %count = ();
my #lst = split(",",$text);
my $i = 0 ;
my #Authors = split(",", $Author);
foreach my $SingleAuthor(#Authors)
{
foreach my $dig (#lst)
{
$count{$SingleAuthor}{$dig}++;
}
}
counter(\%count);
sub counter
{
my $ref = shift;
my #SingleAuthors = keys %$ref;
my %list1;
foreach my $SingleAuthor1(#SingleAuthors)
{
%list1 = $ref->{$SingleAuthor1};
foreach my $dig1 (keys %list1)
{
print $ref->{$SingleAuthor1}->{$dig1};
}
}
}
In two places you are attempting to assign a hash reference to a hash, which results in this warning: Reference found where even-sized list expected.
Here are two edits you need:
# I changed {} to ().
# However, you never use %hashes, so I'm not sure why it's here at all.
my %hashes = (1,2,3,40);
# I added %{} around the right-hand side.
%list1 = %{$ref->{$SingleAuthor1}};
See perlreftut for a useful and brief discussion of complex data structures.
For what it's worth, your counter() method could be simplified without loss of readability by dropping the intermediate variables.
sub counter {
my $tallies = shift;
foreach my $author (keys %$tallies) {
foreach my $dig (keys %{$tallies->{$author}}) {
print $tallies->{$author}{$dig}, "\n";
}
}
}
Or, as ysth points out, like this if you don't need the keys:
foreach my $author_digs (values %$tallies) {
print $dig, "\n" for values %$author_digs;
}
When I ran your code, Perl told me exactly what the problem was.
$ ./hash
Reference found where even-sized list expected at ./hash line 7.
Reference found where even-sized list expected at ./hash line 30.
Use of uninitialized value in print at ./hash line 34.
Reference found where even-sized list expected at ./hash line 30.
Use of uninitialized value in print at ./hash line 34.
Reference found where even-sized list expected at ./hash line 30.
Use of uninitialized value in print at ./hash line 34.
If that's unclear, you can turn on diagnostics to get more detailed descriptions.
$ perl -Mdiagnostics hash
Reference found where even-sized list expected at hash line 7 (#1)
(W misc) You gave a single reference where Perl was expecting a list
with an even number of elements (for assignment to a hash). This usually
means that you used the anon hash constructor when you meant to use
parens. In any case, a hash requires key/value pairs.
%hash = { one => 1, two => 2, }; # WRONG
%hash = [ qw/ an anon array / ]; # WRONG
%hash = ( one => 1, two => 2, ); # right
%hash = qw( one 1 two 2 ); # also fine
How do I create a reference to the value in a specific hash key. I tried the following but $$foo is empty. Any help is much appreciated.
$hash->{1} = "one";
$hash->{2} = "two";
$hash->{3} = "three";
$foo = \${$hash->{1}};
$hash->{1} = "ONE";
#I want "MONEY: ONE";
print "MONEY: $$foo\n";
use strict;
use warnings;
my $hash;
$hash->{1} = "one";
$hash->{2} = "two";
$hash->{3} = "three";
my $foo = \$hash->{1};
$hash->{1} = "ONE";
print "MONEY: $$foo\n";
Turn on strict and warnings and you'll get some clues as to what's going wrong.
use strict;
use warnings;
my $hash = { a => 1, b => 2, c => 3 };
my $a = \$$hash{a};
my $b = \$hash->{b};
print "$$a $$b\n";
In general, if you want to do things with slices or taking refs, you've got to use the old style, piled sigil syntax to get what you want. You may find the References Quick Reference handy, if you don't recall the piled sigil syntax details.
update
As murugaperumal points out, you can do my $foo = \$hash->{a}; I could swear I tried that and it didn't work (to my surprise). I'll chalk it up to being fatigue making me extra foolish.
a classic, and yet the examples don't seem to be complete until you illustrate it both ways
use strict;
use warnings;
my $hash = { abc => 123 };
print $hash->{abc} . "\n"; # 123 , of course
my $ref = \$hash->{abc};
print $$ref . "\n"; # 123 , of course
$hash->{abc} = 456;
print $$ref . "\n"; # 456 , change in the hash reflects in the $$ref
$$ref = 789;
print $hash->{abc} . "\n"; # 789 , change in the $$ref also reflects in the hash
PS: despite being an old topic, I decided to throw my two cents, since I saw I've visited this same question before