Looping over associative array in Perl - perl

Probably a super simple problem, getting the super helpful error message when trying to loop over a nested associative array:
Global symbol "%t" requires explicit package name
Code is:
use strict;
use Data::Dumper;
my %test;
$test{"1"}{"stuff"} = "foo";
$test{"2"}{"stuff"} = "bar";
Then dumping the second level arrays is fine:
foreach my $t (values %test){
print Dumper($t);
}
Which gives:
$VAR1 = {
'stuff' => 'foo'
};
$VAR1 = {
'stuff' => 'bar'
};
But as soon as i try accessing the third level strings, i get the above error, no matter what combination of $'s, #'s, %'s, qoutes or brackets i use.
foreach my $t (values %test){
print Dumper($t{"stuff"});
}

You need to do $t->{"stuff"}. $t is a hash reference (not an actual hash) so you need to use the dereferencing operator.

What you get are hash references, and as such, you'll have to dereference them:
foreach my $t (values %test){
print Dumper($t->{'stuff'})
}
In perl %t, #t and $t (and &t, but that's special in most cases) are all different variable names. For element access you use the corresponding paren type to say which variable you mean:
$t{boo} # accesses %t
$t[800] # accesses #t

Related

Why can't I reference a (particular) Perl array as I'd expect?

I am attempting to use the following to iterate through an array of arrays:
foreach my $elem ( #{ $mastermap } )
{
say $elem;
say $elem[0];
say Dumper($elem);
}
(All just debug output at this point, not what I actually want to do with the array data.)
The output I'm getting (repeated for each loop iteration) is something like:
ARRAY(0x55dabc740cc0)
Use of uninitialized value in say at test.pl line 39.
$VAR1 = [
'bob',
'*',
'1492',
'1492',
'machine acct',
'/var/bob',
'/bin/false'
];
So $elem is an array (also tried treating it as a hash, which was wrong), and Dumper can output the contents of the array, but $elem[0] is undefined? Please tell me what I'm misunderstanding about arrays (probably quite a bit). In case it helps, $mastermap is (I think) an array of arrays, read in using Text::CSV as follows:
my $mastermap = csv ({ in => $passwd, sep_char => ":", quote_char => "#" });
where $passwd is more or less a copy of /etc/passwd.
$elem[0] tries to access to the 1st item in the array #elem. What you actually want to is to access the 1st item in the array reference $elem. The correct syntax to do so is:
$elem->[0]
or
$$elem[0]
You should always add use strict; and use warnings; to the beginning of your Perl scripts/programs. Doing so would have produced the following warning:
Global symbol "#elem" requires explicit package name (did you forget to declare "my #elem"?) at...

Understanding the 'foreach' syntax for the keys of a hash

I have created a simple Perl hash
Sample.pl
$skosName = 'foo';
$skosId = 'abc123';
$skosFile{'type'}{$skosId} = $skosName;
Later on I try to print the hash values using foreach.
This variant works
foreach $skosfile1type ( keys %{skosFile} ){
print ...
}
While this one doesn't
foreach $skosfile1type ( keys %{$skosFile} ) {
print ...
}
What is the difference between the two foreach statements?
In particular, what is the significance of the dollar sign $ in the statement that doesn't work?
Is it something to do with scope, or perhaps my omission of the my or our keywords?
%{skosfile} is the same as %skosfile. It refers to a hash variable with that name. Usually that form isn't used for a simple variable name, but it's allowable.
%{$skosfile} means to look at the scalar variable $skosfile (remember, in perl, $foo, %foo, and #foo are distinctvariables), and, expecting $skosfile to be a hashref, it returns the hash that the reference points to. It is equivalent to %$skosfile, but in fact any expression that returns a hashref can appear inside of %{...}.
The syntax %{ $scalar } is used to tell Perl that the type of $scalar is a hash ref and you want to undo the reference. That is why you need the dollar sign $: $skosfile is the variable you are trying to dereference.
In the same fashion, #{ $scalar } serves to dereference an array.
Although it does not work for complex constructions, in simple cases you may also abbreviate %{$scalar} to %$scalar and #{$scalar} to #$scalar.
In the case of the expression keys %{$skosfile}, keys needs a hash which you obtain by dereferencing $skosfile, a hash ref. In fact, the typical foreach loop for a hash looks like:
foreach my $key ( keys %hash ) {
# do something with $key
}
When you iterate a hash ref:
foreach my $key ( keys %{ $hashref } ) {
# do something with $key
}

How do I define an anonymous scalar ref in Perl?

How do I properly define an anonymous scalar ref in Perl?
my $scalar_ref = ?;
my $array_ref = [];
my $hash_ref = {};
If you want a reference to some mutable storage, there's no particularly neat direct syntax for it. About the best you can manage is
my $var;
my $sref = \$var;
Or neater
my $sref = \my $var;
Or if you don't want the variable itself to be in scope any more, you can use a do block:
my $sref = do { \my $tmp };
At this point you can pass $sref around by value, and any mutations to the scalar it references will be seen by others.
This technique of course works just as well for array or hash references, just that there's neater syntax for doing that with [] and {}:
my $aref = do { \my #tmp }; ## same as my $aref = [];
my $href = do { \my %tmp }; ## same as my $href = {};
Usually you just declare and don't initialize it.
my $foo; # will be undef.
You have to consider that empty hash refs and empty array refs point to a data structure that has a representation. Both of them, when dereferenced, give you an empty list.
perldata says (emphasis mine):
There are actually two varieties of null strings (sometimes referred to as "empty" strings), a defined one and an undefined one. The defined version is just a string of length zero, such as "" . The undefined version is the value that indicates that there is no real value for something, such as when there was an error, or at end of file, or when you refer to an uninitialized variable or element of an array or hash. Although in early versions of Perl, an undefined scalar could become defined when first used in a place expecting a defined value, this no longer happens except for rare cases of autovivification as explained in perlref. You can use the defined() operator to determine whether a scalar value is defined (this has no meaning on arrays or hashes), and the undef() operator to produce an undefined value.
So an empty scalar (which it didn't actually say) would be undef. If you want it to be a reference, make it one.
use strict;
use warnings;
use Data::Printer;
my $scalar_ref = \undef;
my $scalar = $$scalar_ref;
p $scalar_ref;
p $scalar;
This will output:
\ undef
undef
However, as ikegami pointed out, it will be read-only because it's not a variable. LeoNerd provides a better approach for this in his answer.
Anyway, my point is, an empty hash ref and an empty array ref when dereferenced both contain an empty list (). And that is not undef but nothing. But there is no nothing as a scalar value, because everything that is not nothing is a scalar value.
my $a = [];
say ref $r; # ARRAY
say scalar #$r; # 0
say "'#$r'"; # ''
So there is no real way to initialize with nothing. You can only not initialize. But Moose will turn it to undef anyway.
What you could do is make it maybe a scalar ref.
use strict;
use warnings;
use Data::Printer;
{
package Foo;
use Moose;
has bar => (
is => 'rw',
isa => 'Maybe[ScalarRef]',
predicate => 'has_bar'
);
}
my $foo = Foo->new;
p $foo->has_bar;
p $foo;
say $foo->bar;
Output:
""
Foo {
Parents Moose::Object
public methods (3) : bar, has_bar, meta
private methods (0)
internals: {}
}
Use of uninitialized value in say at scratch.pl line 268.
The predicate gives a value that is not true (the empty string ""). undef is also not true. The people who made Moose decided to go with that, but it really doesn't matter.
Probably what you want is not have a default value, but just make it a ScalarRef an required.
Note that perlref doesn't say anything about initializing an empty scalar ref either.
I'm not entirely sure why you need to but I'd suggest:
my $ref = \undef;
print ref $ref;
Or perhaps:
my $ref = \0;
#LeoNerd's answer is spot on.
Another option is to use a temporary anonymous hash value:
my $scalar_ref = \{_=>undef}->{_};
$$scalar_ref = "Hello!\n";
print $$scalar_ref;

Why can't I assign a variable to a hash entry in Perl?

Sorry, I'm super rusty with Perl. See the following code:
foreach my $hash (keys %greylist)
{
$t = $greylist{$hash};
print $greylist{$hash}[4] . "\n";
print $t[4] . "\n";
}
Why does $t[4] evaluate to a blank string, yet $greylist{$hash}[4] which should be the same thing evaluates to an IP address?
$greylist{$hash} contains an array reference. When you do:
print $greylist{$hash}[4];
Perl automatically treats it as an array reference but when you do:
$t = $greylist{$hash};
print $t[4];
You're assigning the array reference to a scalar variable, $t, then attempting to access the 5th element of another variable, #t. use strict would give you an error in this scenario.
Use the arrow operator, ->, to dereference:
$t = $greylist{$hash};
print $t->[4];
perlreftut has a note about this:
If $aref holds a reference to an array, then $aref->[3] is the fourth element of the array. Don't confuse this with $aref[3] , which is the fourth element of a totally different array, one deceptively named #aref . $aref and #aref are unrelated the same way that $item and #item are.

How to manipulate a hash-ref with Perl?

Take a look at this code. After hours of trial and error. I finally got a solution. But have no idea why it works, and to be quite honest, Perl is throwing me for a loop here.
use Data::Diff 'Diff';
use Data::Dumper;
my $out = Diff(\#comparr,\#grabarr);
my #uniq_a;
#temp = ();
my $x = #$out{uniq_a};
foreach my $y (#$x) {
#temp = ();
foreach my $z (#$y) {
push(#temp, $z);
}
push(#uniq_a, [$temp[0], $temp[1], $temp[2], $temp[3]]);
}
Why is it that the only way I can access the elements of the $out array is to pass a hash key into a scalar which has been cast as an array using a for loop? my $x = #$out{uniq_a}; I'm totally confused. I'd really appreciate anyone who can explain what's going on here so I'll know for the future. Thanks in advance.
$out is a hash reference, and you use the dereferencing operator ->{...} to access members of the hash that it refers to, like
$out->{uniq_a}
What you have stumbled on is Perl's hash slice notation, where you use the # sigil in front of the name of a hash to conveniently extract a list of values from that hash. For example:
%foo = ( a => 123, b => 456, c => 789 );
$foo = { a => 123, b => 456, c => 789 };
print #foo{"b","c"}; # 456,789
print #$foo{"c","a"}; # 789,123
Using hash slice notation with a single element inside the braces, as you do, is not the typical usage and gives you the results you want by accident.
The Diff function returns a hash reference. You are accessing the element of this hash that has key uniq_a by extracting a one-element slice of the hash, instead of the correct $out->{uniq_a}. Your code should look like this
my $out = Diff(\#comparr, \#grabarr);
my #uniq_a;
my $uniq_a = $out->{uniq_a};
for my $list (#$uniq_a) {
my #temp = #$list;
push #uniq_a, [ #temp[0..3] ];
}
In the documentation for Data::Diff it states:
The value returned is always a hash reference and the hash will have
one or more of the following hash keys: type, same, diff, diff_a,
diff_b, uniq_a and uniq_b
So $out is a reference and you have to access the values through the mentioned keys.