Filling hash by reference in a procedure - perl

I'm trying to call a procedure, which is filling a hash by reference. The reference to the hash is given as a parameter. The procedure fills the hash, but when I return, the hash is empty. Please see the code below.
What is wrong?
$hash_ref;
genHash ($hash_ref);
#hash is empty
sub genHash {
my ($hash_ref)=(#_);
#cut details; filling hash in a loop like this:
$hash_ref->{$lid} = $sid;
#hash is generetad , filled and i can dump it
}

You might want to initialize hashref first,
my $hash_ref = {};
as autovivification happens inside function to another lexical variable.
(Not so good) alternative is to use scalars inside #_ array which are directly aliased to original variables,
$_[0]{$lid} = $sid;
And btw, consider use strict; use warnings; to all your scripts.

The caller's $hash_ref is undefined. The $hash_ref in the sub is therefore undefined too. $hash_ref->{$lid} = $sid; autovivifies the sub's $hash_ref, but nothing assigns that hash reference to the caller's $hash_ref.
Solution 1: Actually passing in a hash ref to assign to the caller's $hash_ref.
sub genHash {
my ($hash_ref) = #_;
...
}
my $hash_ref = {};
genHash($hash_ref);
Solution 2: Taking advantage of the fact that Perl passes by reference.
sub genHash {
my $hash_ref = $_[0] ||= {};
...
}
my $hash_ref;
genHash($hash_ref);
-or-
genHash(my $hash_ref);
Solution 3: If the hash is going to be empty initially, why not just create it in the sub?
sub genHash {
my %hash;
...
return \%hash;
}
my $hash_ref = genHash();

Related

Passing a hash to a subroutine without changing it input

I'm trying to debug some strange behavior while handling with a hash in Perl.
I'm passing a hash (not ref) to a subroutine and for some reason it updates it.
some_sub($a,%{$hash});
sub some_sub {
my ($a,%hash) = #_;
my #struct;
while (my ($dir, $data) = each %hash) {
foreach my $id (keys(%{$data})) {
my $entry = $data->{$id};
$entry->{id} = $id;
my $parent = $data->{$entry->{id}};
unless ($parent) {
push #struct, $entry
} else {
push #{$parent->{children}},$entry;
}
}
}
}
my %h= %{$hash};
print Dumper(\%h);
The sub some_sub does change %hash but only for the inner scope, so it should not change the data of the outside %hash. Also, I pass the hash as a hash and not as a hash ref. I suspected the sub some_sub inserts memory addresses into the %hash, but I'm not sure.
How should I debug and solve this issue?
EDIT: I also tried to pass a hash ref to the subroutine and do a dereferencing of the hash ref into another hash while doing all of the operations on the new hash.
Every value in a hash is a scalar. If you have a nested hash, the inner hash is stored as a scalar - a hash reference. Therefore, when changing the nested structures, the changes happen in the referenced hash, which is referenced from the original hash, too.
#! /usr/bin/perl
use warnings;
use strict;
sub change {
my %hash2 = #_;
for my $key (keys %hash2) {
++$_ for values $hash2{$key};
}
}
my %hash = (a => {b => 12, c => 24});
change(%hash);
use Data::Dumper; print Dumper \%hash;
Output:
$VAR1 = {
'a' => {
'b' => 13,
'c' => 25
}
};
The process of obtaining a structure that's similar as the original but contains different references is called cloning or deep copying. See Clone or dclone from Storable.
Arguments are passed to a function as a flat list of scalars, so
some_sub($a, %{$hashref})
has the keys and values of the hash passed as a list after $a
some_sub($a, key, value, ...);
since a function call always takes merely a list of scalars.
These key-value pairs are assigned to a hash in the function so when you work with that hash you directly use references from the calling code, your hash values. So data in the caller gets changed if those references are written to.
The details aren't given but in general one way to avoid changing caller's data in the sub is by introducing local variables for each reference the processing encounters. But then those may themselves contain references so you'd still need to be very careful.
It is simpler to make a full deep copy of the hash, ff the data structure isn't huge. For example
use Storable qw(dclone);
some_sub($v, $hashref);
sub some_sub {
my ($var, $hr) = #_;
my $cloned_hashref = dclone($hr);
# work away with $cloned_hashref
}

Trouble passing hash and variable to subroutine

I want to pass a hash and a variable to a subroutine:
%HoA = {'1'=>'2'};
my $group_size = 10;
&delete_unwanted(\%HoA,$group_size);
sub delete_unwanted {
my (%HoA,$group_size) = #_;
print "'$group_size'\n"
}
But, this prints nothing.
You're passing a hash reference (as you should), so therefore assign it to a scalar in your parameter catching:
sub delete_unwanted {
my ($hashref, $group_size) = #_;
print "'$group_size'\n"
}
If you later want to dereference it, you can my %newHoA = %$hashref;, but that will be a copy of the original hash. To access the original structure, just use the reference: print $hashref->{a_key};.
Your problem is in:
my (%HoA,$group_size) = #_;
You can solve it by saying, for example:
sub delete_unwanted {
my $hashPointer = shift;
my $group_size = shift
Note that you can retrieve the original hash inside the subroutine by either: de-referencing the hashPointer (my %HoA = %$hashPointer), or you can access the hash contents directly using the pointer directly (eg, $hashPointer->{'key'})

Initializing hash reference in perl

The following Perl code prints Value:0. Is there a way to fix it other than by adding a dummy key to the hash before hash reference is passed to the subroutine ?
#!/usr/bin/perl
use warnings;
use strict;
my $Hash;
#$Hash->{Key1} = 1234;
Init($Hash);
printf("Value:%d\n",$Hash->{Key});
sub Init
{
my ($Hash) = #_;
$Hash->{Key}=10;
}
Initialize an empty hash reference.
#!/usr/bin/perl
use warnings;
use strict;
my $Hash = {};
Init($Hash);
printf("Value:%d\n",$Hash->{Key});
sub Init
{
my ($Hash) = #_;
$Hash->{Key}=10;
}
I know that an answer has already been accepted, but I figured it was worth explaining why the program acted this way in the first place.
The hash is not created until the second line of the Init function ($Hash->{Key}=10), which automatically creates a hash and stores a reference in the $Hash scalar. This scalar is local to the function, and has nothing to do with the $Hash variable in the body of the script.
This can be changed by modifying the way that the Init function handles its arguments:
sub Init {
my $Hash = $_[0] = {};
$Hash->{'Key'} = 10;
}

How to get the output data from perl subroutine passing a reference

How to get the data output from a perl subroutine..? Pass an hash reference to subroutine "get_data"... fill the data inside the subroutine.. and it should reflect outside.
ex:
my %myhash = ();
get_data(\%myhash);
use strict;
use warnings;
use Data::Dumper;
my %myhash = ();
get_data(\%myhash); #pass hash ref
$myhash{k2} = "Hello SO"; #add one more key value
print Dumper($hash_ref); #Dump hash ref
sub get_data{
my $hash_ref = shift; #get hash ref
$hash_ref->{k1} = "adding one more key value"; #fill data
}
output:
$VAR1 = {
'k2' => 'Hello SO',
'k1' => 'adding one more key calue'
};
You are passing the hash by reference, any changes in the hash would be visible outside the subroutine as well.
Did you face any problem with this code?

Perl woes - assigning and returning a hashes

I have an instance variable, properties, that is being declared and instantiated like so:
$self->{properties}{$key1} = $value;
My understanding that this will declare the properties field, and also set it to a Hash primitive, containing one key value pair.
I'm trying to write a getter for the properties instance variable, that will return the hash:
sub getProperties{
my $self = shift;
my %myhash = $self->{properties};
return %myhash;
}
And subsequently call the getter like so:
my %properties = $properties->getProperties();
When I try to compile this i get:
"Odd number of elements in hash assignment at 70..."
line 70 being: my %myhash = $self->{properties};
In this line of code:
my %myhash = $self->{properties};
%myhash is a hash whereas $self->{properties} is a hash reference. So you're effectively returning a hash with one key/value pair where the key is a reference to a hash and the value is undef.
If you really want to return a hash, do this:
my %myhash = %{$self->{properties}};
Alternatively, return a hash reference. That's generally preferable to returning a hash, since it doesn't make a copy of the original hash and therefore is more memory efficient as the hash gets larger. Here's how it looks:
sub getProperties {
my $self = shift;
return $self->{properties};
}
Then in your calling code instead of this:
my %properties = $properties->getProperties();
$somevalue = $properties{'somekey'};
do this:
# getProperties returns a reference, so assign to a scalar
# variable ($foo) rather than a hash (%foo)
my $properties = $properties->getProperties();
# Use -> notation to dereference the hash reference
$somevalue = $properties->{'somekey'};
isn't $self->{properties} a hashref not a hash?
$ perl t4.pl
size -> 42
t4.pl
#!/usr/bin.perl
use strict;
use warnings;
use t4;
my $t4 = t4->new();
my %hash = $t4->getProperties();
for my $key (keys %hash) {
print "$key -> $hash{$key}\n";
}
t4.pm
package t4;
sub new {
my $class = shift;
my $self = {};
$self->{properties}{size} = 42;
bless ($self, $class);
}
sub getProperties {
my $self = shift;
my %myhash = %{$self->{properties}};
return %myhash;
}
1;