Additional simplifications are possible e.g. no need to loop over the word list three times, and why use hashrefs and make yourself do extra typing when you could use a regular hash.
#!/usr/bin/perl
use strict;
use warnings;
use Data::Dumper;
my @wordsInOrder;
while (<>) {
push @wordsInOrder, split /\W+/, lc($_);
}
my (%single, %double, %triple);
my $index = 0;
foreach my $word (@wordsInOrder) {
$single{$word}++;
my $next_word = $wordsInOrder[$index+1];
if($next_word) {
$double{"$word $next_word"}++;
}
my $next_next_word = $wordsInOrder[$index+2];
if($next_next_word) {
$triple{"$word $next_word $next_next_word"}++;
}
$index++;
}
foreach my $singlet (sort_by_frequency(\%single)) {
print "$singlet\n";
foreach my $doublet (sort_by_frequency(\%double)) {
next unless $doublet =~ /^$singlet\b/;
print "\t$doublet\n";
foreach my $triplet (sort_by_frequency(\%triple)) {
next unless $triplet =~ /^$doublet\b/;
print "\t\t$triplet\n";
}
}
}
sub sort_by_frequency {
my $h = shift;
return sort { $h->{$b} <=> $h->{$a} } keys %$h;
}
This also only matches whole words rather than fragments. Best wishes with the wedding anyway!