Reputation: 442
I'm still learning Perl, so there's probably a more efficient way of doing this. I'm trying to take a hash, reverse it so $values => $keys, grab the new key (the old value) and then sort those keys.
Here's the code in question:
foreach my $key (sort keys reverse %hash){
...}
What I'm expecting to happen is that reverse %hash will return a hash type, which is what keys is looking for. However, I get the following error:
Type of arg 1 to keys must be hash (not reverse)
I've tried putting parentheses around reverse %hash, but still get the same thing.
Any ideas why this wouldn't work?
Upvotes: 3
Views: 5741
Reputation: 385915
Pre 5.14, keys
returns the keys of hash. That requires a hash. You didn't provide one. reverse
doesn't return a hash. In fact, it's impossible to return a hash as only scalars can be returned. (Internally, Perl can put hashes directly on the stack, but that will never be visible to the user without causing a "Bizarre" error message.) This error is detected at compile-time.
5.14 is more flexible. It will also accept a reference to a hash. (It will also accept arrays and references to arrays, but that's not relevant here.) References are scalars, so they can be returned by functions. Your code will actually make it to run-time, but whatever your reverse
returns in scalar context won't a reference to a hash that doesn't exist, so your code will die at that point.
Do you have a reason to want to reverse the hash?
foreach my $key (sort { $hash{$a} cmp $hash{$b} } keys %hash) {
my $val = $hash{$key};
...
}
If you do,
foreach my $val (sort keys %{ { reverse %hash } }) {
# No access to original key
...
}
or
my %flipped = reverse %hash;
foreach my $val (sort keys %flipped) {
my $key = $flipped{$val};
...
}
Upvotes: 3
Reputation: 67890
The argument to keys
must be a hash, array or expression, not a list. If you did
keys { reverse %hash }
you would get the result you expect, because the brackets create an anonymous hash. Parens, on the other hand, only change the precedence. Or, in this case they are probably considered related to the function keys()
, as most perl functions have optional parens.
Also, if you just want the values of the hash, you can use:
values %hash
See the documentation for reverse, values and keys for more info.
Upvotes: 4
Reputation: 118625
Perl functions can either return scalar values or lists; there is no explicit hash return type
(You can call return %hash
from a subroutine, but Perl implicitly unrolls the key-value pairs from the hash and returns them as a list).
Therefore, the return value of reverse %hash
is a list, not a hash, and not suitable for use as an argument to keys
. You can force Perl to interpret this list as a hash with the %{{}}
cast:
foreach my $key (sort keys %{{reverse %hash}}) { ...
You could also sort on the values of the hash by saying
foreach my $key (sort values %hash) { ...
Using values %hash
is subtly different from using keys %{{reverse %hash}}
in that keys %{{reverse %hash}}
will not return any duplicate values.
Upvotes: 7
Reputation: 477150
I think you're describing exactly the situation in this example:
#!/usr/local/bin/perl
use strict;
use warnings;
my %hash = (one => 1, two => 2, three => 3, four => 4);
%hash = reverse %hash;
foreach my $key (sort {$a <=> $b} keys %hash) {
print "$key=>$hash{$key}, ";
}
print "\n";
# it displays: 1=>one, 2=>two, 3=>three, 4=>four
Upvotes: 3