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
}
Related
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
I love hash slices and use them frequently:
my %h;
#h{#keys}=#vals;
Works brilliantly! But 2 things have always vexed me.
First, is it possible to combine the 2 lines above into a single line of code? It would be nice to declare the hash and populate it all at once.
Second, is it possible to slice an existing anonymous hash... something like:
my $slice=$anonh->{#fields}
First question:
my %h = map { $keys[$_] => $vals[$_] } 0..$#keys;
or
use List::MoreUtils qw( mesh );
my %h = mesh #keys, #vals;
Second question:
If it's ...NAME... for a hash, it's ...{ $href }... for a hash ref, so
my #slice = #hash{#fields};
is
my #slice = #{ $anonh }{#fields};
The curlies are optional if the reference expression is a variable.
my #slice = #$anonh{#fields};
Mini-Tutorial: Dereferencing Syntax
References quick reference
perlref
perlreftut
perldsc
perllol
For your first question, to do it in a single line of code:
#$_{#keys}=#vals for \my %h;
or
map #$_{#keys}=#vals, \my %h;
but I wouldn't do that; it's a confusing way to write it.
Either version declares the variable and immediately takes a reference to it and aliases $_ to the reference so that the hash reference can be used in a slice. This lets you declare the variable in the existing scope; #{ \my %h }{#keys} = #vals; also "works", but has the unfortunate drawback of scoping %h to that tiny block in the hash slice.
For your second question, as shown above, slices can be used on hash references; see http://perlmonks.org/?node=References+quick+reference for some easy to remember rules.
my #slice = #$anonh{#fields};
or maybe you meant:
my $slice = [ #$anonh{#fields} ];
but #slice/$slice there is a copy of the values. To get an array of aliases to the hash values, you can do:
my $slice = sub { \#_ }->( #$anonh{#fields} );
Hash slice syntax is
# <hash-name-or-hash-ref> { LIST }
When you are slicing a hash reference, enclose it in curly braces so it doesn't get dereferenced as an array. This gives you:
my #values = #{$anonh}{#fields}
for a hash reference $anonh.
I'm busy learning Perl at the moment and I've been given some code to look at and "solve".
foreach $field (keys %$exam)
The code above is the area Im having difficulty in understanding. I thought $ was scalar and % was a hash and so I'm unsure what %$ is.
Any help appreciated!
Thanks guys.
%$exam says that you are using not a normal hash, but a dereferenced one, i.e. somewhere before this statement $exam became the reference of a hash (for example $exam = \%somehash or $exam = { a => 1 } for an anonymous hashref). Now, in order to use the previously referenced hash you have to use this syntax to dereference it. To use it unambiguously, it could be written as %{$exam}.
$exam = {a=>1, b=>2}; # anonym hash, $exam is ref for this hash
In order to use this ref like hash you have to use dereferencing operator % before ref
foreach $field (keys %$exam)
For example the same for array ref.
$a = [1,2,3,4]; # anonym arr, $a is ref for this array
So that you have to use operator # before ref $a for dereferencing
foreach $element (#$a) {print $element;}
This is the syntax for dereferencing $exam reference variable.
See
http://perldoc.perl.org/perlreftut.html
http://perldoc.perl.org/perlref.html
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.
I'm trying to collect the values that I store in a hash of hashes, but I'm kinda confused in how perl does that. So, I create my hash of hashes as follows:
my %hash;
my #items;
#... some code missing here, generally I'm just populating the #items list
#with $currentitem items
while (<FILE>) { #read the file
($a, $b) = split(/\s+/,$_,-1);
$hash{$currentitem} => {$a => $b};
print $hash{$currentitem}{$a} . "\n";#this is a test and it works
}
The above code seems to work. Now, to the point: I have an array #items, which keeps the $currentitem values. And I want to do something like this:
#test = keys %hash{ $items[$num] };
So that I can get all the key/value pairs for a specific item. I've tried the line of code above, as well as
while ( ($key, $value) = each( $hash{$items[$num]} ) ) {
print "$key, $value\n";
}
I've even tried to populate the hash as follows:
$host{ "$currentcustomer" }{"$a"} => "$b";
Which seems to be more correct according to the various online sources I've met. But still, I can't access the data inside that hash... Any ideas?
I am confused by you saying that this works:
$hash{$currentitem} => {$a => $b};
That shouldn't work (and doesn't work for me). The => operator is a special kind of comma, not an assignment (see perlop). In addition, the construct on the right makes a new anonymous hash. Using that, a new anonymous hash would overwrite the old one for each element you tried to add. You would only ever have one element for each $currentitem.
Here is what you want for assignment:
$hash{$currentitem}{$a} = $b;
And here is how to get the keys:
keys %{ $hash{ $items[$num] } };
I suggest reading up on Perl references to get a better handle on this. The syntax can be a bit tricky at first.
Long answer is in perldoc perldsc.
Short answer is:
keys %{ $expr_producing_hash_ref };
In your case I believe it's
keys %{ $hash{$items[$num]} };