As far as fuzzy matching goes, I posted code once that showed how to do fuzzy matching with Perl regexes. Here's an updated version:
sub mk_fuzzy {
our ($m, $i, $d) = splice @_, 0, 3;
use re 'eval';
qr{
(?{ [ $i, $d, $m ] })
^
@{[ map $_[$_] =~ /!$/ ? substr($_[$_],0,-1) : qq{
(?:
$_[$_] (?: | (?(?{ \$^R->[0] }) @{[ $_ < $#_ and "(?! $_[$_+1]
+ )" ]} (?s: . ) (?{ [ \$^R->[0] - 1, \$^R->[1], \$^R->[2] ] }) | (?!)
+ ) )
| (?(?{ \$^R->[1] }) (?{ [ \$^R->[0], \$^R->[1] - 1, \$^R->[2]
+ ] }) | (?!) )
| (?(?{ \$^R->[2] }) (?! $_[$_] ) (?s: . ) (?{ [ \$^R->[0], \$
+^R->[1], \$^R->[2] - 1 ] }) | (?!) )
)
}, 0 .. $#_ ]}
$
(?{ [ [$m-$^R->[2], $m], [$i-$^R->[0], $i], [$d-$^R->[1], $d] ] })
}x;
}
The use of the function is as follows:
my $test = mk_fuzzy(
1, # max number of modifications to allow
1, # max number of insertions to allow
1, # max number of deletions to allow
qw( p e r l )
);
for my $word (qw( pearl earl pearly peely )) {
if ($word =~ $test) {
print "$word is close enough to 'perl'\n";
}
else {
print "$word isn't enough like 'perl'\n";
}
}
This reports that pearl, earl, and peely are close to "perl".
The reason I send the letters of the word individually is because the function allows you to follow a letter with a ! which means it MUST appear in the word. (And the 'letters' don't have to be just letters, they could be multi-character strings.) Example:
my $test = mk_fuzzy(
1, # max number of modifications to allow
1, # max number of insertions to allow
1, # max number of deletions to allow
qw( p e r! l ) # must have the 'r' in this relative location
);
for my $word (qw( pearl earl pearly peely )) {
if ($word =~ $test) {
print "$word is close enough to 'perl'\n";
}
else {
print "$word isn't enough like 'perl'\n";
}
}
This one only reports pearl and earl, since peely didn't keep the 'r'.
After a successful match, by the way, you can inspect $^R to see how many modifications, insertions, and deletions were necessary:
if ($word =~ $test) {
my ($m_used, $m_allowed) = @{ $^R->[0] };
my ($i_used, $i_allowed) = @{ $^R->[1] };
my ($d_used, $d_allowed) = @{ $^R->[2] };
print "Using $m_used mods, $i_used inserts, and $d_used dels, $word
+matched\n";
}
|