About the usage of hash reference in Perl - perl

This reports syntax error:
$hash={a=>2};
print %{$hash}{a};
But this works:
print each(%{$hash})
Why??

To get an element from a hashref, you take the normal code for getting a hash element: $foo{'bar'}, and replace the name of the hash, not including the sigil, with the hashref: $$hash{'bar'}. Your % would only be used to dereference to the full hash, as in your each case, not just an element.
More helpful hints at http://perlmonks.org/?node=References+quick+reference.

Maybe this will help you understand why it's wrong...
$hash = {a => 2}; #Works: $hash is a reference to the hash
%foo = %{$hash}; #Now, we've dereferenced the hash to %foo
# Wherever we have "$hash", we can now use "foo"...
print %foo{a}; #Whoops! Doesn't work.
print %hash{a}; #And, neither did this!
print $foo{a}; #No problem! Use '$" when talking about a single hash element
print ${$hash}{a} #Same as above.
print each %foo; #Each takes a hash (with "%" sign)
print each %{$hash}; #Same as above.
print $hash->{a} #Syntactic Sugar: Same as ${$hash{a}} or $$hash{a}

Yeah, just like print %hash{a} doesn't work even though each(%hash) does.
each(%hash) ==> each(%{ $ref })
print($hash{a}) ==> print(${ $ref }{a})

You were missing the lookup '->'.
print %{$hash}{a};
should be:
print %{$hash}->{a};
You declare it as $ but then try to cast to a hash and retrieve the value, not sure why.
Just retrieve like so:
print $hash->{a};
My personal preference when it comes to hashes:
$hash1->{a} = 1;
print $hash1->{a}, "\n"; # prints '1'
Multi level:
$hash2->{a}{a} = 1;
$hash2->{a}{b} = 2;
print $hash2->{a}{a}, "\n"; # prints '1'
print $hash2->{a}{b}, "\n"; # prints '2'
Looping:
while (my ($key, $value) = each %{$hash1})
{
print $key, "\n"; # prints 'a'
print $value, "\n"; # prints '1'
}

Related

Perl: Passing by reference does not modify the hash

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.

How do I return an array and a hashref?

I want to make a subroutine that adds elements (keys with values) to a previously-defined hash. This subroutine is called in a loop, so the hash grows. I don’t want the returning hash to overwrite existing elements.
At the end, I would like to output the whole accumulated hash.
Right now it doesn’t print anything. The final hash looks empty, but that shouldn’t be.
I’ve tried it with hash references, but it does not really work. In a short form, my code looks as follows:
sub main{
my %hash;
%hash=("hello"=>1); # entry for testing
my $counter=0;
while($counter>5){
my(#var, $hash)=analyse($one, $two, \%hash);
print ref($hash);
# try to dereference the returning hash reference,
# but the error msg says: its not an reference ...
# in my file this is line 82
%hash=%{$hash};
$counter++;
}
# here trying to print the final hash
print "hash:", map { "$_ => $hash{$_}\n" } keys %hash;
}
sub analyse{
my $one=shift;
my $two=shift;
my %hash=%{shift #_};
my #array; # gets filled some where here and will be returned later
# adding elements to %hash here as in
$hash{"j"} = 2; #used for testing if it works
# test here whether the key already exists or
# otherwise add it to the hash
return (#array, \%hash);
}
but this doesn’t work at all: the subroutine analyse receives the hash, but its returned hash reference is empty or I don’t know. At the end nothing got printed.
First it said, it's not a reference, now it says:
Can't use an undefined value as a HASH reference
at C:/Users/workspace/Perl_projekt/Extractor.pm line 82.
Where is my mistake?
I am thankful for any advice.
Arrays get flattened in perl, so your hashref gets slurped into #var.
Try something like this:
my ($array_ref, $hash_ref) = analyze(...)
sub analyze {
...
return (\#array, \#hash);
}
If you pass the hash by reference (as you're doing), you needn't return it as a subroutine return value. Your manipulation of the hash in the subroutine will stick.
my %h = ( test0 => 0 );
foreach my $i ( 1..5 ) {
do_something($i, \%h);
}
print "$k = $v\n" while ( my ($k,$v) = each %h );
sub do_something {
my $num = shift;
my $hash = shift;
$hash->{"test${num}"} = $num; # note the use of the -> deference operator
}
Your use of the #array inside the subroutine will need a separate question :-)

Traversing Array of Hashes in perl

I have a structure as below:
my $var1 = [{a=>"B", c=>"D"}, {E=>"F", G=>"H"}];
Now I want to traverse the first hash and the elements in it.. How can I do it?
When I do a dumper of $var1 it gives me Array and when on #var1 it says a hash.
You iterate over the array as you would with any other array, and you'll get hash references. Then iterate over the keys of each hash as you would with a plain hash reference.
Something like:
foreach my $hash (#{$var1}) {
foreach my $key (keys %{$hash}) {
print $key, " -> ", $hash->{$key}, "\n";
}
}
First off, you're going to trip Perl's strict mode with your variable declaration that includes barewords.
With that in mind, complete annotated example given below.
use strict;
my $test = [{'a'=>'B','c'=>'D'},{'E'=>'F','G'=>'H'}];
# Note the #{ $test }
# This says "treat this scalar reference as a list".
foreach my $elem ( #{ $test } ){
# At this point $elem is a scalar reference to one of the anonymous
# hashes
#
# Same trick, except this time, we're asking Perl
# to treat the $elem reference as a reference to hash
#
# Hence, we can just call keys on it and iterate
foreach my $key ( keys %{ $elem } ){
# Finally, another bit of useful syntax for scalar references
# The "point to" syntax automatically does the %{ } around $elem
print "Key -> $key = Value " . $elem->{$key} . "\n";
}
}
C:\wamp\bin\perl\bin\PERL_2~1\BASIC_~1\REVISION>type traverse.pl
my $var1=[{a=>"B", c=>"D"},{E=>"F", G=>"H"}];
foreach my $var (#{$var1}) {
foreach my $key (keys(%$var)) {
print $key, "=>", $var->{$key}, "\n";
}
print "\n";
}
C:\wamp\bin\perl\bin\PERL_2~1\BASIC_~1\REVISION>traverse.pl
c=>D
a=>B
G=>H
E=>F
$var1 = [] is a reference to an anonymous array
using the # sigil before it as in $var1 gives you the access to the array it is referencing. So analogous to foreach (#arr) {...} you would do foreach (#{$var1}) {...}.
Now, the elements in the array that you have provided #{$var1} are anonymous (means not named) too, but they are anonymous hashes, so just like with the arrayref, here we do %{$hash_reference} to get access to the hash referenced by $hash_reference. Here, $hash_reference is $var.
After accessing the hash using %{$var} it becomes easy to access the keys of the hash using keys(%$var) or keys(%{$var}). Since the result returned is an array of keys therefore we can use keys(%{$var}) inside foreach (keys(%{$var})) {...}.
We access the scalar value inside an anonymous hash by using a key like $hash_reference->{$keyname}, that's all the code did.
In case your array contained anonymous hashes of arrays like :
$var1=[ { akey=>["b", "c"], mkey=>["n", "o"]} ];
then, this is how you will access the array values:
C:\wamp\bin\perl\bin\PERL_2012\BASIC_PERL\REVISION>type traverse.pl
my $var1=[ {akey=>["b", "c"], mkey=>["n", "o"]} ];
foreach my $var (#{$var1}) {
foreach my $key (keys(%$var)) {
foreach my $elem (#{ $var->{$key} }) {
print "$key=>$elem,";
}
print "\n...\n";
}
print "\n";
}
C:\wamp\bin\perl\bin\PERL_2012\BASIC_PERL\REVISION>traverse.pl
mkey=>n,mkey=>o,
...
akey=>b,akey=>c,
...
Practice it more and regularly, it will soon become easy for you to break complex structures into such combinations. This is how I created a large parser for another software, it is full of answers to your questions :)
With one peek at amon's up-voted comment above (thanks, amon!) I was able to write this little ditty:
#!/usr/bin/perl
# Given an array of hashes, print out the keys and values of each hash.
use strict; use warnings;
use Data::Dump qw(dump);
my $var1=[{A=>"B",C=>"D"},{E=>"F",G=>"H"}];
my $count = 0;
# #{$var1} is the array of hash references pointed to by $var1
foreach my $href (#{$var1})
{
print "\nArray index ", $count++, "\n";
print "=============\n";
# %{$href} is the hash pointed to by $href
foreach my $key (keys %{$href})
{
# $href->{$key} ( ALT: $$href{$key} ) is the value
# corresponding to $key in the hash pointed to by
# $href
# print $key, " => ", $href->{$key}, "\n";
print $key, " => ", $$href{$key}, "\n";
}
print "\nCompare with dump():\n";
dump ($var1);
print "\nJust the first hash (index 0):\n";
# $var1->[0] ( ALT: $$var1[0] ) is the first hash reference (index 0)
# in #{$var1}
# dump ($var1->[0]);
dump ($$var1[0]);
#print "\nJust the value of key A: \"", $var1->[0]->{A}, "\"\n";
#print "\nJust the value of key A: \"", $var1->[0]{A}, "\"\n";
print "\nJust the value of key A: \"", $$var1[0]{A}, "\"\n"

Perl Variable References in Hash / Foreach

I would like to change variables stored in a hash, but I kept receiving the error:
"Can't use the string ("SCALAR(0x30f558)") as a SCALAR ref while "strict refs" in use at - line 14.
My simplified code is as follows:
#!/usr/bin/perl
use strict;
use warnings;
my $num = 1234;
my $a = 5;
my %hash = (\$num => "value");
foreach my $key (keys %{hash}){
print "Key: $key\n";
#OPTION1: $a = $$key;
}
my $ref = \$num ;
print "Ref: $ref\n";
#OPTION2: $a = $$ref ;
print $a;
Running this prints:
Key: SCALAR(0x30f558)
Ref: SCALAR(0x30f558)
5
showing that both $key and $ref are pointing to the same variable - $num
Also, the code on OPTION1 and OPTION2 are identical if $key and $ref are the same.
When I uncomment OPTION2, $a prints out as 1234.
When I uncomment OPTION1, however, I receive the error shown above.
QUESTION:
How do I change $a to $num using the hash as I tried to do in OPTION1? And why will this not work as is?
References:
http://cpansearch.perl.org/src/CHIPS/perl5.004_05/t/pragma/strict-refs
I followed this code closely:
use strict 'refs' ;
my $fred ;
my $b = \$fred ;
my $a = $$b ;
which posed no error until I introduced the hash.
Thank you for your help.
Original Code (doesn't work):
#User Defined - here are the defaults
my $a = 122160;
my $b = 122351;
my $c = 'string';
my $d = 15;
my $e = 123528;
#etc.
#Create variable/print statement hash
my %UserVariables = (
\$a => "A: (Default: $a): ",
\$b => "B: (Default: $b): ",
\$c => "C: (Default: $c): ",
\$d => "D: (Default: $d): ",
\$e => "E: (Default: $e): ",
);
#Allow user to change variables if desired
foreach (keys %UserVariables){
print $UserVariables{$_};
chomp (my $temp = <>);
print "$_\n";
$$_ = $temp unless ($temp eq '');
print "$temp\n" unless ($temp eq '');
};
Less efficient method that does work:
#Alternate Method without loops (not ideal)
my $temp;
print $UserVariables{\$a};
chomp ($temp = (<>));
$a= $temp unless ($temp eq '');
print $UserVariables{\$b};
chomp ($temp = (<>));
$b= $temp unless ($temp eq '');
print $UserVariables{\$c};
chomp ($temp = (<>));
$c= $temp unless ($temp eq '');
print $UserVariables{\$d};
chomp ($temp = (<>));
$d= $temp unless ($temp eq '');
print $UserVariables{\$e};
chomp ($temp = (<>));
$e= $temp unless ($temp eq '');
Perl hash keys can only be string. You don't have reference as key, but what your reference automatically stringified to: a verbatim string "SCALAR(0x30f558)" instead. Obviously, string won't work as reference.
You should rethink the way you store data and maybe explain in little more details what you want to do instead on focusing on how.
In your particular case illustrated by example just use plain hash for those values you want to be overridable:
my %config = (
a => 122160,
b => 122351,
c => 'string',
d => 15,
e => 123528,
);
...and then overwrite values in this hash.
I would like to change variables stored in a hash
Just like you can't store variables in a scalar, you can't store variables in hashes. You can store values (including references to variables) in hashes. (Such as the string value in your code.)
showing that both $key and $ref are pointing to the same variable - $num
No. It shows that the values of $key and $ref have the same stringification.
When I uncomment OPTION1, however, I receive the error shown above.
Hash table keys are necessarily strings, just like array keys are necessarily non-negative integers.
I followed this code closely:
No, the same with hashes works fine.
use strict 'refs' ;
my %hash = ( fred => undef );
my $b = \$hash{fred} ;
my $a = $$b ;
I can't provide a solution, because you didn't say what you are trying to do.
You keep saying in the comments that you want to modify variables passed through STDIN, however it is not clear what you mean by this. Are you passing command line arguments or requesting input from the user or piping the output of another program into yours, or something else entirely? The ideal approach will vary slightly depending on what you're trying to do.
To read command line arguments you access the #ARGV array:
$foo = $ARGV[0]; #read the first argument
To read a line off of STDIN:
print "Enter a number: ";
$foo = <STDIN>;

Iterate through hash values in perl

I've got a hash with let's say 20 values.
It's initialized this way:
my $line = $_[0]->[0];
foreach my $value ($line) {
print $value;
}
Now when I try to get the value of each hash in $line it says:
Use of uninitialized value in print at file.pl line 89
Is there a way to iterate through each value of a hash?
I also tried it with:
my %line = $_[0]->[0];
foreach my $key (keys %line) {
print %line->{$key};
}
But that is also not working:
Reference found where even-sized list expected at file.pl at line 89
Anybody knows what to do? It shouldn't be that difficult...
To iterate over values in a hash:
for my $value (values %hash) {
print $value;
}
$line in your first example is a scalar, not a hash.
If it's a hash reference, dereference it with %{$line}.
First, you must understand the difference between a hash, and a hash reference.
Your initial assignment $_[0]->[0] means something like : Takes the first argument of the current function ($_[0]), dereference it (->) and consider it is an array and retrieves it's first value ([0]). That value can not be a list or a hash, it must be a scalar (string, int, float, reference).
Here is some example:
my %hash = ( MyKey => "MyValue");
my $hashref = \%hash;
# The next line print all elements of %hash
foreach (keys %hash) { print $_ }
# And is equivalent to
foreach (keys %{$hashref}) { print $_ }
$hash{MyKey} == $hashref->{MyKey}; # is true
Please refer to http://perldoc.perl.org/perlreftut.html for further details.
The warning is telling you that there nothing at $_[0]->[0]. It's not dying and telling you that you're indexing nothing, so $_[0] is likely an arrayref, but nothing is in the first slot--or perhaps it's pointing to an empty array.
Were it a empty string or a 0, it wouldn't complain.
Were there any reference there, you could print something even if only: BLAH(0x80af74). (Where "BLAH" is one of "ARRAY", "HASH", "SCALAR", "REF", "GLOB", "IO", ... )
My suggestion is that you do this:
use Data::Dumper;
say Data::Dumper->Dump( [ $_[0] ] ); # or even say Data::Dumper->Dump( [ \#_ ] )
and then look at the output.
Once you've got a hashref at $_[0]->[0], then if you must loop through the hash, the best way is:
while ( my ( $key, $value ) = each %$hashref ) {
do_stuff_with_key_and_value( $key, $value );
}
see each
Lastly, it seems that you have some sigil confusion. See the last part of this link for a decent attempt to explain that sigils ( '$', '#', '%' ) are not part of the name of a variable, but indicators what we want retrieved from it. Perl compilation woes