I'm reading in a file of words and need to hash words to a key if they are anagrams. So if I read in dog, I sort the word to become dgo. And this would be my key. So I read in the word god, it would be sorted to dgo as well and they should both hash to the same key.
Here is what I am trying but I am not sure if I am doing this correctly.
if(exists $hash{$string})
{
@values2 = $hash{$string};
push @values2, $original;
for my $word (@values2)
{
print $word."\n";
}
#print "Hello";
}
else
{
@values = ();
$hash {$string} = @values;
push @values, $string;
}
}
So the $string
is my sorted word, the key. So if the key doesn't exist, i create a new array at that key for my $hash. i then push the orginal word into the array. But if the key already exists, I then get the array from the hash and push or add the next word.
But this isn't working properly. Can I not do this?
The thing you need to understand is: There is no such thing as a two dimensional data structure in perl. You don't have a hash of arrays, you have a hash of array references .
This can lead to some pretty subtle gotchas.
This for example - isn't doing what you think:
@values = ();
$hash{$string} = @values;
push @values, $string;
It's emptying @values
. But then it's assigning it in a scalar context. Which means you're setting:
$hash{$string} = 0;
And then inserting $string
into @values
, but that's making no difference to the hash, because you've set the hash value to the size of your empty array.
Likewise this:
@values2 = $hash{$string};
push @values2, $original;
for my $word (@values2) {
print $word. "\n";
}
You're only ever going to be retrieving an array reference at best (but if you've populated with the else
block it's not even that - it's just 0
) which means your for loop isn't going to work. $hash{$key}
can only ever be a single value.
If you want to set a hash key to an array;
$hash{$string} = [@values];
If you want to add elements:
push ( @{$hash{$string}}, @values );
And if you want to extract elements;
my @array = @{ $hash{$string} };
You need the extra sigil, because that's how you tell perl 'work with a reference'. ( You can also use ->
notation in some circumstances. I've omitted this to avoid confusing the issue )
Yes, you can do this. But you cannot store an array in a hash, you have to store a reference to it.
push @{ $hash{$string} }, $original;
To retrieve the array, derefence the value:
print join ' ', @{ $hash{dgo} }; # dog god
Basic Perl data structures are about single values. The variable $foo
can only store a single value. The array @foo
can store an array of single values. The hash %foo
has a key that points to a single value.
If you need more (such as a key that points to multiple values), you need to know about Perl References . A Perl reference is a Perl data structure (such as a hash or array) where each entry points not to a single value, but to another structure.
In your case, you'd like your key (the word dgo
) to point to an array of words that have those letters.
Imagine something like this:
my @dgo_words = qw(dog dgo god gdo odg ogd); # All possible combinations
$words{dgo} = \@dgo_words; # The '\' means this is a reference to @dgo_words
Now, words{dgo}
points to a reference to the array @dgo_words
. If dereference the reference (by putting the correct prefix on the variable), I can get back to the array:
my @array_of_dgo_words = @{ $words{dgo} };
Remember, $words{dgo}
points to an array, and putting the @
in front gives me access to that array. The curly braces in this particular case are optional:
my @array_of_dgo_words = @$words{dgo};
Personally, I prefer the braces because it highlights the fact this is a reference. Others feel that eliminating them makes the code easier to read.
If @{ $words{dgo} }
is my array, I could use push
to add words to the array:
push @{ $words{dgo} }, 'dog';
Now, dog
is added to the array referenced by $words{dgo}
.
Here's a simple program:
#! /usr/bin/env perl
use strict;
use warnings;
use feature qw(say);
my %words;
#
# First add all the words into the correct key
#
while ( my $word = <DATA> ) {
chomp $word;
my $key = join '', sort split //, $word;
push @{ $words{$key} }, $word;
}
for my $group ( sort keys %words ) { # For each key in my word hash
my @word_group = @{ $words{$group} }; # Dereference to get the list of words
say qq(Words for group "'$group":);
for my $word ( @word_group ) { # This loop prints out the words
say " $word";
}
}
__DATA__
dog
bog
save
god
gob
vase
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.