File Coverage

blib/lib/Text/Levenshtein.pm
Criterion Covered Total %
statement 53 55 96.3
branch 26 26 100.0
condition 10 12 83.3
subroutine 9 9 100.0
pod 2 2 100.0
total 100 104 96.1


line stmt bran cond sub pod time code
1             package Text::Levenshtein;
2             $Text::Levenshtein::VERSION = '0.13';
3 9     9   307033 use 5.006;
  9         33  
4 9     9   44 use strict;
  9         656  
  9         189  
5 9     9   41 use warnings;
  9         22  
  9         224  
6 9     9   45 use Exporter;
  9         12  
  9         465  
7 9     9   49 use Carp;
  9         13  
  9         650  
8 9     9   42 use List::Util ();
  9         21  
  9         5433  
9              
10             our @ISA = qw(Exporter);
11             our @EXPORT = ();
12             our @EXPORT_OK = qw(distance fastdistance);
13             our %EXPORT_TAGS = ();
14              
15              
16             sub distance
17             {
18 128 100 100 128 1 50935 my $opt = pop(@_) if @_ > 0 && ref($_[-1]) eq 'HASH';
19 128 100       615 croak "distance() takes 2 or more arguments" if @_ < 2;
20 126         287 my ($s,@t)=@_;
21 126         150 my @results;
22              
23 126 100       263 $opt = {} if not defined $opt;
24              
25 126         196 foreach my $t (@t) {
26 134         270 push(@results, fastdistance($s, $t, $opt));
27             }
28              
29 126 100       449 return wantarray ? @results : $results[0];
30             }
31              
32             my $eq_with_diacritics = sub {
33             my ($x, $y) = @_;
34             return $x eq $y;
35             };
36              
37             my $eq_without_diacritics;
38              
39             # This is the "Iterative with two matrix rows" version
40             # from the wikipedia page
41             # http://en.wikipedia.org/wiki/Levenshtein_distance#Computing_Levenshtein_distance
42             sub fastdistance
43             {
44 254 100 100 254 1 47613 my $opt = pop(@_) if @_ > 0 && ref($_[-1]) eq 'HASH';
45 254 100       915 croak "fastdistance() takes 2 or 3 arguments" unless @_ == 2;
46 251         379 my ($s, $t) = @_;
47 251         278 my (@v0, @v1);
48 0         0 my ($i, $j);
49 0         0 my $eq;
50              
51 251 100       503 $opt = {} if not defined $opt;
52 251 100       492 if ($opt->{ignore_diacritics}) {
53 12 100       26 if (not defined $eq_without_diacritics) {
54 1         4455 require Unicode::Collate;
55 1         51878 my $collator = Unicode::Collate->new(normalization => undef, level => 1);
56             $eq_without_diacritics = sub {
57 164     164   416 return $collator->eq(@_);
58 1         30698 };
59             }
60 12         17 $eq = $eq_without_diacritics;
61             }
62             else {
63 239         314 $eq = $eq_with_diacritics;
64             }
65              
66 251 100       569 return 0 if $s eq $t;
67 231 100 66     1068 return length($s) if !$t || length($t) == 0;
68 228 100 66     926 return length($t) if !$s || length($s) == 0;
69              
70 223         298 my $s_length = length($s);
71 223         263 my $t_length = length($t);
72              
73 223         525 for ($i = 0; $i < $t_length + 1; $i++) {
74 1242         2572 $v0[$i] = $i;
75             }
76              
77 223         461 for ($i = 0; $i < $s_length; $i++) {
78 1008         1232 $v1[0] = $i + 1;
79              
80 1008         2019 for ($j = 0; $j < $t_length; $j++) {
81             # my $cost = substr($s, $i, 1) eq substr($t, $j, 1) ? 0 : 1;
82 5068 100       13921 my $cost = $eq->(substr($s, $i, 1), substr($t, $j, 1)) ? 0 : 1;
83 5068         48936 $v1[$j + 1] = List::Util::min(
84             $v1[$j] + 1,
85             $v0[$j + 1] + 1,
86             $v0[$j] + $cost,
87             );
88             }
89              
90 1008         2150 for ($j = 0; $j < $t_length + 1; $j++) {
91 6076         13981 $v0[$j] = $v1[$j];
92             }
93             }
94              
95 223         762 return $v1[ $t_length];
96             }
97              
98             1;
99              
100             __END__