How to reuse a variable, which is a reference to a hash? - perl

I'm trying to add a few hashes to the array, reusing the same variable (in my real program, I'm doing this in a loop, that's why reusing the var). This is the code:
my #items;
my %x;
$x{'aa'} = 'bb';
push(#items, \%x);
%x = (); # I think the error is here, I'm not resetting the reference :(
$x{'cc'} = 'dd';
push(#items, \%x);
use Data::Dumper;
print Dumper \#items;
However, what I see is not what I expect:
$VAR1 = [
{
'cc' => 'dd'
},
$VAR1->[0]
];
What do I misunderstand?
ps. This is how it looks in the loop:
my #items;
my %x;
foreach ... {
%x = ();
$x{'aa'} = some_random_number();
push(#items, \%x);
}

Reusing a hash reference that way is not possible, it will always point to the same hash. And if you reset the same hash over and over and add new values to it, of course the beginning of the hash will be deleted.
You are correct that %x = () is the problem, because that is where you delete the content in %x (the 'aa' key).
What you want is to create a new hash reference for each value you want to store.
use strict;
use warnings;
my #items = qw( .... );
foreach ... {
my %x; # use a lexically scoped variable, which will be new each iteration
$x{'aa'} = some_random_number();
push(#items, \%x);
}
Or even:
my %x = (aa => some_random());
Or better yet, use a hash reference right away
my $x = { aa => some_random() };
push #items, $x;
Or again, a bit quicker:
push #items, { aa => some_random() };
{ ... } creates an anonymous hash ref. Reusing variables is not a good idea, hardly ever. Unless you are using so many variables that you are afraid of memory issues. Use the lexical scope and anonymous references to your advantage to encapsulate your code and avoid confusion.
But since you are using different keys, and pushing them onto an array, I feel like you are confused about Perl data structures. You could just use the same hash, if your keys are different:
my %x;
foreach ... {
$x{$key} = some_random_number();
}
You probably want a variable to keep track of the key name there, not a constant.
In the comments you describe a practice of adding values to the hash, and then adding it to the array to start over. This is exactly why you should use a lexically scoped hash. For example:
....
for my $table (#tables) {
my %hash; # <--- new variable for each $table
for my $stuff (#stuff) {
$hash{$stuff} = something();
}
....
push #array, \%hash;
}
If you put an enclosure around the hash, it will be reset automatically, and then use the "same" name space the following iterations, except it will point to a different memory location (a new data reference).
Or if, as you say, you cannot use a loop outside, you can just put a block around the variable.
{ # start of a block
my %hash;
for my $stuff (#stuff) {
$hash{$stuff} = something();
}
....
push #array, \%hash;
} # end of a block
my %hash; # this "%hash" is a different variable from the previous

Related

Understanding Perl pointers to hashes in my script

So I know how the split is working, my question is how the pointer $p is working here. Does it have a different value for each iteration, and the value gets pushed on to the hash as an array? Is that how it will keep those values together when I need to extract them? I have over 100 lines of values I need to reference back to and I'm not sure how it will do that if $p is not changing with each iteration. Thanks!
else{
my($site,$x,$y) = split /,/, $_;
my $p;
$p->{site} = $site;
$p->{x} = $x;
$p->{y} = $y;
push #{$die_loc{$pattern}}, $p;
}
I take it that this is all in a loop where $_ is assigned every time through.
You declare my $p every time so each gets its own memory location once it is assigned to. At that point it is autovivified into a hash reference, since that's how it is assigned. That reference is copied onto the array, so you will have them all. You can get the memory address of a reference using refaddr from the core module Scalar::Util. Or, for that matter, just print $p.
What you have can be written as
my $p = { site => $site, x => $x, y => $y };
push #{$die_loc{$pattern}}}, $p;
So after all is said and done, the hash %die_loc will under key $pattern have an array reference, which has for elements the hash references with keys site, x, and y.
use feature 'say';
foreach my $hr (#{$die_loc{$pattern}}) {
say "site: $hr->{site}, x: $hr->{x}, y: $hr->{y}"
}
This prints a line for each (presumed) iteration that you processed. But generally you don't want to type key names but rather use keys to print a hash, for example
foreach my $hr (#{$die_loc{$pattern}}) {
say join ', ', map { "$_: $hr->{$_}" } sort keys %$hr;
}
where keys are also sorted for consistent prints. Or use a module, like Data::Dump.
Note that references are a little different from pointers.
There is only a code fragment posted so let me also say that you want to always start with
use warnings 'all';
use strict;
That code is much better written like this
else {
my ( $site, $x, $y ) = split /,/;
my %p = (
site => $site,
x => $x,
y => $y,
);
push #{ $die_loc{$pattern} }, \%p;
}
or, perhaps better still
else {
my %p;
#p{qw/ site x y /} = split /,/;
push #{ $die_loc{$pattern} }, \%p;
}

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

Updated: Initialise and Clear multiple hash in one line

How can I Initialise and clear multiple hash in one line.
Ex:
my %hash1 = ();
my %hash2 = ();
my %hash3 = ();
my %hash4 = ();
to
my ( %hash1, %hash2, %hash3, %hash4 ) = ?
It appears (from your comments) that you really want to empty hashes that already have stuff in them. You can do it like this:
(%hash1,%hash2,%hash3) = ();
Complete example:
use strict;
use warnings;
my %hash1 = ('foo' => 1);
my %hash2 = ('bar' => 1);
my %hash3 = ('baz' => 1);
(%hash1,%hash2,%hash3) = ();
print (%hash1,%hash2,%hash3);
A variable declaration always gives you an empty variable, so there is no need to set it to empty. This is true even in a loop:
for (0..100)
{
my $x;
$x++;
print $x;
}
This will print 1 over and over; even though you might expect $x to retain its value, it does not.
Explanation: Perl allows list assignment like ($foo,$bar) = (1,2). If the list on the right is shorter, any remaining elements get assigned undef. Thus assigning the empty list to a list of variables makes them all undefined.
Another useful way to set a bunch of things is the x operator:
my ($x,$y,$z) = (100)x3;
This sets all three variables to 100. It doesn't work so well for hashes, though, because each one needs a list assigned to it.
It's as simple as doing
my ( %hash1, %hash2, %hash3, %hash4 );
and they will not contain any keys or values at that point.
The same technique applies to scalars and arrays.
To undef multiple hashes, you could do
undef %$_ for ( \%hash1, \%hash2 );
You can initialize it as:
my %hash1 = %hash2 = %hash3 = %hash4 = ();
You do not need to assign anything to a new variable in order to assure it is empty. All variables are empty, if nothing has been assigned to them.
my %hash; # hash contains nothing
%hash = () # hash still contains nothing
The only time it would be useful to assign the empty list to a hash is if you want to remove previously assigned values. And even then, that would only be a useful thing to do if it could not already be solved by applying the correct scope restriction to the hash.
my (%hash1, %hash2);
while (something) {
# some code
(%hash1,%hash2) = (); # delete old values
}
Emptying the hashes. Better written as:
while (something) {
my (%hash1, %hash2); # all values are now local to the while loop
# some code
}

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.

Put an array into a hash of hashes, transforming it into a Hash itself

my %hash = #array;
transforms an array into a hash, but how can do the same with $hash{something}?
$hash{something} = { #array };
The {} around it creates a hashref.
When you write:
my %hash = #array;
What Perl sees is:
my %hash = ($array[0], $array[1], ... $array[$#array]);
So the #array is expanded into a list, and that list is assigned to the plural %hash. The list must have an even number of elements, or you will get a warning (assuming you are using use warnings; in your script, which you always should. use strict; as well).
Broken down even more, it is:
my %hash;
$hash{$array[0]} = $array[1];
$hash{$array[2]} = $array[3];
...
$hash{$array[$#array - 1]} = $array[$#array];
So with the translation from #array to %hash explained, to insert this hash into a hash of hashes at a particular key:
$HoH{key} = \%hash;
Where the \ character takes a reference to %hash. If %hash is not needed elsewhere you could constrain it with a block:
{
my %hash = #array;
$HoH{key} = \%hash;
}
Using a do {...} makes it a little shorter:
$HoH{key} = do {my %hash = #array; \%hash};
But not much shorter. If the above seems tedious to you, it should. In Perl, the above construct can be reduced to:
$HoH{key} = { #array };
Where the {...} denotes the anonymous hash reference constructor, equivalent to
do {my %hash = (...); \%hash}
In addition to {...}, Perl provides the [...] construct to create anonymous array references. The ... of both constructs sees list context.