How is the scalar $keys getting value 1 instead of undef? - perl

Folks,
As far as my understanding goes, exists function would check for existence of a key in a hash. So for the below mentioned situation, key1 or key2 have not been defined. Going by that the hash reference $var has no keys.
In which case upon calling keys(%{$var}) should return undef.
HOWEVER, its returning 1. How..what am I missing here ?
my $var;
if (exists $var->{key1}->{key2}) {
$var->{key1}->{key2} = 1;
}
my $keys = keys(%{$var});
print $keys; #prints 1 to output console

The fact that you're checking $var->{key1}->{key2} creates $var->{key1} as empty hashref. This can be seen by doing:
use Data::Dumper;
my $var = {};
if (exists $var->{key1}->{key2}) {
print "cannot happen\n"
}
print Dumper($var);
Which prints:
$VAR1 = {
'key1' => {}
};
So, the scalar of keys is 1, because there is one key.

This is autovivification. Note that you can disable autovivification for your whole script, or for a particular lexical scope, by using the no autovification; pragma.

Related

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 :-)

How to parse through many hashes using foreach?

foreach my %hash (%myhash1,%myhash2,%myhash3)
{
while (($keys,$$value) = each %hash)
{
#use key and value...
}
}
Why doesn't this work :
it says synta error on foreach line.
Pls tell me why is it wrong.
This is wrong because you seem to think that this allows you to access each hash as a separate construct, whereas what you are in fact doing is, besides a syntax error, accessing the hashes as a mixed-together new list. For example:
my %hash1 = qw(foo 1 bar 1);
my %hash2 = qw(abc 1 def 1);
for (%hash1, %hash2) # this list is now qw(foo 1 bar 1 abc 1 def 1)
When you place a hash (or array) in a list context statement, they are expanded into their elements, and their integrity is not preserved. Some built-in functions do allow this behaviour, but normal Perl code does not.
You also cannot assign a hash as the for iterator variable, that can only ever be a scalar value. What you can do is this:
for my $hash (\%myhash1, \%myhash2, \%myhash3) {
while (my ($key, $value) = each %$hash) {
...
Which is to say, you create a list of hash references and iterate over them. Note that you cannot tell the difference between the hashes with this approach.
Note also that I use my $hash because this variable must be a scalar.
The syntax should be like:
my $hash1 = {'a'=>1};
my $hash2 = {'b'=>1};
my #arr2 = ($hash1, $hash2);
foreach $hash (#arr2)
{
while(($key, $value) = each %$hash)
{
print $key, $value;
}
}
you need to reference and then dereference the hash.

How to use a 'subroutine reference' as a hash key

In Perl, I'm learning how to dereference 'subroutine references'. But I can't seem to use a subroutine reference as a hash 'key'.
In the following sample code,
I can create a reference to a subroutine ($subref) and then dereference it to run the subroutine (&$subref)
I can use the reference as a hash 'value' and then easily dereference that
But I cannot figure out how to use the reference as a hash 'key'. When I pull the key out of the hash, Perl interprets the key as a string value (not a reference) - which I now understand (thanks to this site!). So I've tried Hash::MultiKey, but that seems to turn it into an array reference. I want to treat it as a subroutine/code reference, assuming this is somehow possible?
Any other ideas?
use strict;
#use diagnostics;
use Hash::MultiKey;
my $subref = \&hello;
#1:
&$subref('bob','sue'); #okay
#2:
my %hash;
$hash{'sayhi'}=$subref;
&{$hash{'sayhi'}}('bob','sue'); #okay
#3:
my %hash2;
tie %hash2, 'Hash::MultiKey';
$hash2{$subref}=1;
foreach my $key (keys %hash2) {
print "Ref type is: ". ref($key)."\n";
&{$key}('bob','sue'); # Not okay
}
sub hello {
my $name=shift;
my $name2=shift;
print "hello $name and $name2\n";
}
This is what is returned:
hello bob and sue
hello bob and sue
Ref type is: ARRAY
Not a CODE reference at d:\temp\test.pl line 21.
That is correct, a normal hash key is only a string. Things that are not strings get coerced to their string representation.
my $coderef = sub { my ($name, $name2) = #_; say "hello $name and $name2"; };
my %hash2 = ( $coderef => 1, );
print keys %hash2; # 'CODE(0x8d2280)'
Tieing is the usual means to modify that behaviour, but Hash::MultiKey does not help you, it has a different purpose: as the name says, you may have multiple keys, but again only simple strings:
use Hash::MultiKey qw();
tie my %hash2, 'Hash::MultiKey';
$hash2{ [$coderef] } = 1;
foreach my $key (keys %hash2) {
say 'Ref of the key is: ' . ref($key);
say 'Ref of the list elements produced by array-dereferencing the key are:';
say ref($_) for #{ $key }; # no output, i.e. simple strings
say 'List elements produced by array-dereferencing the key are:';
say $_ for #{ $key }; # 'CODE(0x8d27f0)'
}
Instead, use Tie::RefHash. (Code critique: prefer this syntax with the -> arrow for dereferencing a coderef.)
use 5.010;
use strict;
use warnings FATAL => 'all';
use Tie::RefHash qw();
my $coderef = sub {
my ($name, $name2) = #_;
say "hello $name and $name2";
};
$coderef->(qw(bob sue));
my %hash = (sayhi => $coderef);
$hash{sayhi}->(qw(bob sue));
tie my %hash2, 'Tie::RefHash';
%hash2 = ($coderef => 1);
foreach my $key (keys %hash2) {
say 'Ref of the key is: ' . ref($key); # 'CODE'
$key->(qw(bob sue));
}
From perlfaq4:
How can I use a reference as a hash key?
(contributed by brian d foy and Ben Morrow)
Hash keys are strings, so you can't really use a reference as the key.
When you try to do that, perl turns the reference into its stringified
form (for instance, HASH(0xDEADBEEF) ). From there you can't get back
the reference from the stringified form, at least without doing some
extra work on your own.
Remember that the entry in the hash will still be there even if the
referenced variable goes out of scope, and that it is entirely
possible for Perl to subsequently allocate a different variable at the
same address. This will mean a new variable might accidentally be
associated with the value for an old.
If you have Perl 5.10 or later, and you just want to store a value
against the reference for lookup later, you can use the core
Hash::Util::Fieldhash module. This will also handle renaming the keys
if you use multiple threads (which causes all variables to be
reallocated at new addresses, changing their stringification), and
garbage-collecting the entries when the referenced variable goes out
of scope.
If you actually need to be able to get a real reference back from each
hash entry, you can use the Tie::RefHash module, which does the
required work for you.
So it looks like Tie::RefHash will do what you want. But to be honest, I don't think that what you want to do is a particularly good idea.
Why do you need it? If you e.g. need to store parameters to the functions in the hash, you can use HoH:
my %hash;
$hash{$subref} = { sub => $subref,
arg => [qw/bob sue/],
};
foreach my $key (keys %hash) {
print "$key: ref type is: " . ref($key) . "\n";
$hash{$key}{sub}->( #{ $hash{$key}{arg} } );
}
But then, you can probably choose a better key anyway.

Perl : Dereference between #_ and $_

I have a question with the following Code:
#!/usr/bin/perl
use strict;
use warnings;
my %dmax=("dad" => "aaa","asd" => "bbb");
my %dmin=("dad" => "ccc","asd" => "ddd");
&foreach_schleife(\%dmax,\%dmin);
sub foreach_schleife {
my $concat;
my $i=0;
foreach my $keys (sort keys %{$_[0]}) {
while ($_[$i]) {
$concat.="$_[$i]{$keys} ";
print $_[$i]{$keys}."\n";
$i++;
}
$i=0;
$concat="";
}
}
The Output is:
bbb
ddd
aaa
ccc
I don't understand this.
Normally you must dereference references on hashes,arrays etc.
Why not here? Its enough to write :
$_[$i]{$keys}."\n";
and not something like that:
$$_[$i]{$keys}."\n";
Why?
Has it something to do with the speciality of the variable #_/$_?
My guess is that because an array (or a hash, for that matter) can only contain hash references your second act of indexing means that the reference is understood.
I think the developers need to document this a little better.
To see that it is not special to *_, you can try this before the loop:
my #a = #_;
And this during:
print $a[$i]{$keys}."\n";
I think the main thing is that if you only have a scalar reference as the base, then at least one -> is required. So
my ( $damxr, $dminr ) = #_;
would require
$dmaxr->{ $key };
The reason why you don't have to dereference $_[0] and $_[1] is that $_[$i]{$keys} is a valid short-hand notation for $_[$i]->{$keys} when your reference is in an array or a hash.
$$_[$i]{$keys} won't work, because it will try to dereference the special variable $_ to a scalar. The correct syntax is %{$_[$i]}, but then you'll have to use %{$_[$i]}->{$keys}, which is more verbose.
#_ is the array of subroutine arguments, hence $_[$index] accesses the element at $index
Dereferencing is only good if you have references, but #_ isn't one.

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