File Coverage

blib/lib/Text/Match.pm
Criterion Covered Total %
statement 74 82 90.2
branch 12 16 75.0
condition 6 6 100.0
subroutine 17 18 94.4
pod 1 6 16.6
total 110 128 85.9


line stmt bran cond sub pod time code
1             #!/usr/bin/perl -I/home/phil/perl/cpan/DataTableText/lib/
2             #-------------------------------------------------------------------------------
3             # Match text question against possible answer strings
4             # Philip R Brenan at appaapps dot com, Appa Apps Ltd Inc., 2021
5             #-------------------------------------------------------------------------------
6             # podDocumentation
7             package Text::Match;
8 1     1   621 use v5.26;
  1         11  
9             our $VERSION = 20201221; # Version
10 1     1   6 use warnings FATAL => qw(all);
  1         2  
  1         32  
11 1     1   4 use strict;
  1         2  
  1         30  
12 1     1   6 use Carp;
  1         2  
  1         114  
13 1     1   575 use Data::Dump qw(dump);
  1         8147  
  1         109  
14 1     1   4033 use Data::Table::Text qw(:all);
  1         151207  
  1         2001  
15 1     1   727 use Math::Permute::List;
  1         512  
  1         74  
16 1     1   8 use feature qw(say current_sub);
  1         3  
  1         969  
17              
18             sub normalizeText($) # Normalize a string of text
19 68     68 0 111 {my ($s) = @_; # String to normalize
20 68         461 split /\s+/, lc $s =~ s(\W) ( )gsr
21             }
22              
23             sub span($$) # Return the length of the span if the first array is spanned by the second array otherwise undef
24 717     717 0 1114 {my ($Q, $A) = @_; # Question, Answer
25 717         818 my @m; my $n = 0;
  717         850  
26 717   100     2056 while(@$A and @$Q) # Each answer word
27 1578 100       2606 {if ($$A[0] eq $$Q[0])
28 473         595 {shift @$Q;
29 473         1095 shift @$A;
30             }
31             else
32 1105         1224 {++$n;
33 1105         2586 shift @$A;
34             }
35             }
36 717 100       1650 @$Q ? undef : $n
37             }
38              
39             sub randomizeArray(@) # Randomize an array
40 0     0 0 0 {my (@a) = @_; # Array
41 0         0 for my $i(keys @a)
42 0         0 {my $j = int ($#a * rand);
43 0         0 my $s = $a[$i]; my $t = $a[$j]; $a[$i] = $t; $a[$j] = $s;
  0         0  
  0         0  
  0         0  
44             }
45             @a
46 0         0 }
47              
48             sub score # Respond to a question with a similar answer
49 34     34 0 78 {my ($Q, $A) = @_; # Question, Answer
50 34         62 my @q = normalizeText $Q;
51 34         70 my @a = normalizeText $A;
52 34         59 my @m;
53 34         74 while(@a) # Each answer word
54 132   100     363 {my $s = span([@q], [@a]) // span([reverse @q], [@a]); # Normal sequence or reversed
55 132 100       280 if (defined $s)
56 34         67 {push @m, [$s, $A];
57             }
58             else # All permutations if necessary
59             {permute
60 472     472   14412 {my $s = span([@_], [@a]);
61 472 100       1132 push @m, [$s, $A] if defined $s;
62 98         424 } @q;
63             }
64 132         1959 shift @a;
65             }
66             @m
67 34         122 }
68              
69             #D1 Match Text # Match some text against possible answers
70             sub response($$) # Respond to a question with a plausible answer
71 15     15 1 48 {my ($Q, $A) = @_; # Question, possible answers
72 15         26 my @m;
73 15         33 for my $A(@$A) # Each possible answer
74 31         68 {push @m, score($Q, $A);
75             }
76 15 50       36 return '' unless @m;
77             my ($m) =
78 28         57 sort { $$a[0] <=> $$b[0]} # Smallest score first
79 15         80 sort {length($$a[1]) <=> length($$b[1])} @m; # Shortest string first
  24         76  
80 15         111 $$m[1]
81             }
82             #d
83             #-------------------------------------------------------------------------------
84             # Export - eeee
85             #-------------------------------------------------------------------------------
86              
87 1     1   10 use Exporter qw(import);
  1         2  
  1         47  
88              
89 1     1   7 use vars qw(@ISA @EXPORT @EXPORT_OK %EXPORT_TAGS);
  1         2  
  1         290  
90              
91             @ISA = qw(Exporter);
92             @EXPORT = qw();
93             @EXPORT_OK = qw(
94             response
95             );
96             %EXPORT_TAGS = (all=>[@EXPORT, @EXPORT_OK]);
97              
98             # podDocumentation
99             =pod
100              
101             =encoding utf-8
102              
103             =head1 Name
104              
105             Text::Match - Match text question against possible answer strings
106              
107             =head1 Synopsis
108              
109             =head1 Description
110              
111             Match text question against possible answer strings
112              
113              
114             Version 20201221.
115              
116              
117             The following sections describe the methods in each functional area of this
118             module. For an alphabetic listing of all methods by name see L.
119              
120              
121              
122             =head1 Match Text
123              
124             Match some text against possible answers
125              
126             =head2 response($Q, $A)
127              
128             Respond to a question with a plausible answer
129              
130             Parameter Description
131             1 $Q Question
132             2 $A Possible answers
133              
134             B
135              
136              
137              
138             is_deeply response("a c", ["a b c", "a b c d"]), "a b c"; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
139              
140              
141             is_deeply response("a c", ["a b c", "a b c d"]), "a b c"; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
142              
143              
144             is_deeply response("a d", ["a b c", "a b c d"]), "a b c d"; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
145              
146              
147              
148             is_deeply response("b d", ["a b c d", "a b c d e"]), "a b c d"; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
149              
150              
151             is_deeply response("b d", ["a b c d", "a b c d e"]), "a b c d"; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
152              
153              
154             is_deeply response("b e", ["a b c d", "a b c d e"]), "a b c d e"; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
155              
156              
157              
158             is_deeply response("c a", ["a b c", "a b c d"]), "a b c"; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
159              
160              
161             is_deeply response("c a", ["a b c", "a b c d"]), "a b c"; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
162              
163              
164             is_deeply response("d a", ["a b c", "a b c d"]), "a b c d"; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
165              
166              
167              
168             is_deeply response("d b", ["a b c d", "a b c d e"]), "a b c d"; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
169              
170              
171             is_deeply response("d b", ["a b c d", "a b c d e"]), "a b c d"; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
172              
173              
174             is_deeply response("e b", ["a b c d", "a b c d e"]), "a b c d e"; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
175              
176              
177              
178             is_deeply response("c a b", ["a b c", "a b c d"]), "a b c"; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
179              
180              
181             is_deeply response("c a d", ["a b c", "a b c d"]), "a b c d"; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
182              
183              
184             is_deeply response("c a b d", ["a b c", "a b c d", "C a b d"]), "a b c d"; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
185              
186              
187              
188              
189             =head1 Index
190              
191              
192             1 L - Respond to a question with a plausible answer
193              
194             =head1 Installation
195              
196             This module is written in 100% Pure Perl and, thus, it is easy to read,
197             comprehend, use, modify and install via B:
198              
199             sudo cpan install TextMatch
200              
201             =head1 Author
202              
203             L
204              
205             L
206              
207             =head1 Copyright
208              
209             Copyright (c) 2016-2021 Philip R Brenan.
210              
211             This module is free software. It may be used, redistributed and/or modified
212             under the same terms as Perl itself.
213              
214             =cut
215              
216              
217              
218             # Tests and documentation
219              
220             sub test
221 1     1 0 18 {my $p = __PACKAGE__;
222 1         12 binmode($_, ":utf8") for *STDOUT, *STDERR;
223 1 50       89 return if eval "eof(${p}::DATA)";
224 1         78 my $s = eval "join('', <${p}::DATA>)";
225 1 50       10 $@ and die $@;
226 1     1   778 eval $s;
  1         72312  
  1         16  
  1         75  
227 1 50       533 $@ and die $@;
228 1         143 1
229             }
230              
231             test unless caller;
232              
233             1;
234             # podDocumentation
235             __DATA__