Skip to content

Commit b7b4b9e

Browse files
authored
Add anagram to generator (#265)
* Add anagram to generator * Add anagram solution
1 parent d4cebf2 commit b7b4b9e

File tree

4 files changed

+241
-85
lines changed

4 files changed

+241
-85
lines changed
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
exercise: Anagram
2+
plan: 15
3+
subs: match_anagrams
4+
tests: |-
5+
for my $case (@test_cases) {
6+
is match_anagrams($case->{input}), $case->{expected}, $case->{description};
7+
}
8+
9+
example: |-
10+
sub match_anagrams {
11+
my ($input) = @_;
12+
13+
return [
14+
grep {
15+
lc $_ ne lc $input->{subject}
16+
&& join( '', sort( split( //, lc $_ ) ) ) eq
17+
join( '', sort( split( //, lc $input->{subject} ) ) )
18+
} @{ $input->{candidates} }
19+
];
20+
}
21+
22+
stub: |-
23+
sub match_anagrams {
24+
my ($input) = @_;
25+
return undef;
26+
}
Lines changed: 11 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,19 @@
11
package Anagram;
22
use strict;
33
use warnings;
4+
use Exporter qw<import>;
5+
our @EXPORT_OK = qw<match_anagrams>;
46

5-
sub match {
6-
my ( $word, @words ) = @_;
7+
sub match_anagrams {
8+
my ($input) = @_;
79

8-
my @results;
9-
my $canonical = _canonize($word);
10-
foreach my $w (@words) {
11-
next if $w eq $word;
12-
my $try = _canonize($w);
13-
if ( $try eq $canonical ) {
14-
push @results, $w;
15-
}
16-
}
17-
return \@results;
18-
}
19-
20-
sub _canonize {
21-
my ($str) = @_;
22-
return join '', sort split //, lc $str;
10+
return [
11+
grep {
12+
lc $_ ne lc $input->{subject}
13+
&& join( '', sort( split( //, lc $_ ) ) ) eq
14+
join( '', sort( split( //, lc $input->{subject} ) ) )
15+
} @{ $input->{candidates} }
16+
];
2317
}
2418

2519
1;
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
package Anagram;
2+
use strict;
3+
use warnings;
4+
use Exporter qw<import>;
5+
our @EXPORT_OK = qw<match_anagrams>;
6+
7+
sub match_anagrams {
8+
my ($input) = @_;
9+
return undef;
10+
}
11+
12+
1;

exercises/practice/anagram/anagram.t

100644100755
Lines changed: 192 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -1,98 +1,222 @@
11
#!/usr/bin/env perl
2-
use strict;
3-
use warnings;
2+
use Test2::V0;
3+
use JSON::PP;
4+
use constant JSON => JSON::PP->new;
45

5-
use Test2::Bundle::More;
6-
use JSON::PP qw(decode_json);
7-
use FindBin qw($Bin);
6+
use FindBin qw<$Bin>;
87
use lib $Bin, "$Bin/local/lib/perl5";
98

10-
my $module = 'Anagram';
9+
use Anagram qw<match_anagrams>;
1110

12-
my $cases;
13-
{
14-
local $/ = undef;
15-
$cases = decode_json scalar <DATA>;
16-
}
17-
18-
plan 3 + @$cases;
19-
20-
#diag explain $cases;
21-
22-
ok -e "$Bin/$module.pm", "missing $module.pm"
23-
or BAIL_OUT(
24-
"You need to create a class called $module.pm with an function called match() that gets the original word as the first parameter and a reference to a list of word to check. It should return a referene to a list of words."
25-
);
11+
my @test_cases = do { local $/; @{ JSON->decode(<DATA>) }; };
12+
plan 15;
2613

27-
eval "use $module";
28-
ok !$@, "Cannot load $module.pm"
29-
or BAIL_OUT("Does $module.pm compile? Does it end with 1; ?");
14+
imported_ok qw<match_anagrams> or bail_out;
3015

31-
can_ok( $module, 'match' )
32-
or BAIL_OUT("Missing package $module; or missing sub match()");
33-
34-
my $sub = $module . '::match';
35-
36-
foreach my $c (@$cases) {
37-
no strict 'refs';
38-
is_deeply $sub->( $c->{word}, @{ $c->{words} } ), $c->{expected},
39-
$c->{name};
16+
for my $case (@test_cases) {
17+
is match_anagrams( $case->{input} ), $case->{expected},
18+
$case->{description};
4019
}
4120

4221
__DATA__
4322
[
4423
{
45-
"word" : "diaper",
46-
"words" : ["hello", "world", "zombies", "pants"],
47-
"expected" : [],
48-
"name" : "no matches"
24+
"description": "no matches",
25+
"expected": [],
26+
"input": {
27+
"candidates": [
28+
"hello",
29+
"world",
30+
"zombies",
31+
"pants"
32+
],
33+
"subject": "diaper"
34+
},
35+
"property": "findAnagrams"
36+
},
37+
{
38+
"description": "detects two anagrams",
39+
"expected": [
40+
"stream",
41+
"maters"
42+
],
43+
"input": {
44+
"candidates": [
45+
"stream",
46+
"pigeon",
47+
"maters"
48+
],
49+
"subject": "master"
50+
},
51+
"property": "findAnagrams"
52+
},
53+
{
54+
"description": "does not detect anagram subsets",
55+
"expected": [],
56+
"input": {
57+
"candidates": [
58+
"dog",
59+
"goody"
60+
],
61+
"subject": "good"
62+
},
63+
"property": "findAnagrams"
64+
},
65+
{
66+
"description": "detects anagram",
67+
"expected": [
68+
"inlets"
69+
],
70+
"input": {
71+
"candidates": [
72+
"enlists",
73+
"google",
74+
"inlets",
75+
"banana"
76+
],
77+
"subject": "listen"
78+
},
79+
"property": "findAnagrams"
80+
},
81+
{
82+
"description": "detects three anagrams",
83+
"expected": [
84+
"gallery",
85+
"regally",
86+
"largely"
87+
],
88+
"input": {
89+
"candidates": [
90+
"gallery",
91+
"ballerina",
92+
"regally",
93+
"clergy",
94+
"largely",
95+
"leading"
96+
],
97+
"subject": "allergy"
98+
},
99+
"property": "findAnagrams"
100+
},
101+
{
102+
"description": "detects multiple anagrams with different case",
103+
"expected": [
104+
"Eons",
105+
"ONES"
106+
],
107+
"input": {
108+
"candidates": [
109+
"Eons",
110+
"ONES"
111+
],
112+
"subject": "nose"
113+
},
114+
"property": "findAnagrams"
49115
},
50116
{
51-
"word" : "ant",
52-
"words" : ["tan", "stand", "at"],
53-
"expected" : ["tan"],
54-
"name" : "detect_simple_anagram"
117+
"description": "does not detect non-anagrams with identical checksum",
118+
"expected": [],
119+
"input": {
120+
"candidates": [
121+
"last"
122+
],
123+
"subject": "mass"
124+
},
125+
"property": "findAnagrams"
55126
},
56127
{
57-
"word" : "master",
58-
"words" : ["stream", "pigeon", "maters"],
59-
"expected" : ["stream", "maters"],
60-
"name" : "multiple_anagrams"
128+
"description": "detects anagrams case-insensitively",
129+
"expected": [
130+
"Carthorse"
131+
],
132+
"input": {
133+
"candidates": [
134+
"cashregister",
135+
"Carthorse",
136+
"radishes"
137+
],
138+
"subject": "Orchestra"
139+
},
140+
"property": "findAnagrams"
61141
},
62142
{
63-
"word" : "galea",
64-
"words" : ["eagle"],
65-
"expected" : [],
66-
"name" : "does_not_confuse_different_duplicates"
143+
"description": "detects anagrams using case-insensitive subject",
144+
"expected": [
145+
"carthorse"
146+
],
147+
"input": {
148+
"candidates": [
149+
"cashregister",
150+
"carthorse",
151+
"radishes"
152+
],
153+
"subject": "Orchestra"
154+
},
155+
"property": "findAnagrams"
67156
},
68157
{
69-
"word" : "good",
70-
"words" : ["dog", "goody"],
71-
"expected" : [],
72-
"name" : "eliminate_anagram_subsets"
158+
"description": "detects anagrams using case-insensitive possible matches",
159+
"expected": [
160+
"Carthorse"
161+
],
162+
"input": {
163+
"candidates": [
164+
"cashregister",
165+
"Carthorse",
166+
"radishes"
167+
],
168+
"subject": "orchestra"
169+
},
170+
"property": "findAnagrams"
73171
},
74172
{
75-
"word" : "listen",
76-
"words" : ["enlists", "google", "inlets", "banana"],
77-
"expected" : ["inlets"],
78-
"name" : "detect_anagram"
173+
"description": "does not detect an anagram if the original word is repeated",
174+
"expected": [],
175+
"input": {
176+
"candidates": [
177+
"go Go GO"
178+
],
179+
"subject": "go"
180+
},
181+
"property": "findAnagrams"
79182
},
80183
{
81-
"word" : "allergy",
82-
"words" : ["gallery", "ballerina", "regally", "clergy", "largely", "leading"],
83-
"expected" : ["gallery", "regally", "largely"],
84-
"name" : "multiple_anagrams"
184+
"description": "anagrams must use all letters exactly once",
185+
"expected": [],
186+
"input": {
187+
"candidates": [
188+
"patter"
189+
],
190+
"subject": "tapper"
191+
},
192+
"property": "findAnagrams"
85193
},
86194
{
87-
"word" : "Orchestra",
88-
"words" : ["cashregister", "Carthorse", "radishes"],
89-
"expected" : ["Carthorse"],
90-
"name" : "anagrams_are_case_insensitive"
195+
"description": "words are not anagrams of themselves (case-insensitive)",
196+
"expected": [],
197+
"input": {
198+
"candidates": [
199+
"BANANA",
200+
"Banana",
201+
"banana"
202+
],
203+
"subject": "BANANA"
204+
},
205+
"property": "findAnagrams"
91206
},
92207
{
93-
"word" : "banana",
94-
"words" : ["banana"],
95-
"expected" : [],
96-
"name" : "same_word_isnt_anagram"
208+
"description": "words other than themselves can be anagrams",
209+
"expected": [
210+
"Silent"
211+
],
212+
"input": {
213+
"candidates": [
214+
"Listen",
215+
"Silent",
216+
"LISTEN"
217+
],
218+
"subject": "LISTEN"
219+
},
220+
"property": "findAnagrams"
97221
}
98222
]

0 commit comments

Comments
 (0)