Skip to content

Commit dd0f79e

Browse files
authored
Merge pull request #138 from exercism/anagram-exercise
anagram-exercise: Added new exercise.
2 parents 4ed10be + f08b7c3 commit dd0f79e

10 files changed

+323
-0
lines changed

config.json

+8
Original file line numberDiff line numberDiff line change
@@ -454,6 +454,14 @@
454454
"practices": [],
455455
"prerequisites": [],
456456
"difficulty": 2
457+
},
458+
{
459+
"slug": "anagram",
460+
"name": "Anagram",
461+
"uuid": "7aa2f51d-451e-453d-9c76-9d3637aaad72",
462+
"practices": [],
463+
"prerequisites": [],
464+
"difficulty": 3
457465
}
458466
]
459467
},
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
# Instructions
2+
3+
Your task is to, given a target word and a set of candidate words, to find the subset of the candidates that are anagrams of the target.
4+
5+
An anagram is a rearrangement of letters to form a new word: for example `"owns"` is an anagram of `"snow"`.
6+
A word is _not_ its own anagram: for example, `"stop"` is not an anagram of `"stop"`.
7+
8+
The target and candidates are words of one or more ASCII alphabetic characters (`A`-`Z` and `a`-`z`).
9+
Lowercase and uppercase characters are equivalent: for example, `"PoTS"` is an anagram of `"sTOp"`, but `StoP` is not an anagram of `sTOp`.
10+
The anagram set is the subset of the candidate set that are anagrams of the target (in any order).
11+
Words in the anagram set should have the same letter case as in the candidate set.
12+
13+
Given the target `"stone"` and candidates `"stone"`, `"tones"`, `"banana"`, `"tons"`, `"notes"`, `"Seton"`, the anagram set is `"tones"`, `"notes"`, `"Seton"`.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
# Introduction
2+
3+
At a garage sale, you find a lovely vintage typewriter at a bargain price!
4+
Excitedly, you rush home, insert a sheet of paper, and start typing away.
5+
However, your excitement wanes when you examine the output: all words are garbled!
6+
For example, it prints "stop" instead of "post" and "least" instead of "stale."
7+
Carefully, you try again, but now it prints "spot" and "slate."
8+
After some experimentation, you find there is a random delay before each letter is printed, which messes up the order.
9+
You now understand why they sold it for so little money!
10+
11+
You realize this quirk allows you to generate anagrams, which are words formed by rearranging the letters of another word.
12+
Pleased with your finding, you spend the rest of the day generating hundreds of anagrams.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
{
2+
"authors": [
3+
"dreig"
4+
],
5+
"contributors": [
6+
"SimaDovakin"
7+
],
8+
"files": {
9+
"solution": [
10+
"anagram.u"
11+
],
12+
"test": [
13+
"anagram.test.u"
14+
],
15+
"example": [
16+
".meta/examples/anagram.example.u"
17+
]
18+
},
19+
"blurb": "Given a word and a list of possible anagrams, select the correct sublist.",
20+
"source": "Inspired by the Extreme Startup game",
21+
"source_url": "https://github.com/rchatley/extreme_startup"
22+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
anagram.findAnagrams : Text -> [Text] -> [Text]
2+
anagram.findAnagrams subject candidates =
3+
use Text toLowercase
4+
use Bag fromText
5+
isAnagram : Text -> Text -> Boolean
6+
isAnagram = on (Bag.==) (toLowercase >> fromText)
7+
8+
List.filter (c -> toLowercase c != toLowercase subject && isAnagram subject c) candidates
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
[
2+
{
3+
"name": "no matches",
4+
"test_code": "anagram.findAnagrams.tests.ex1 = let\n actual = findAnagrams \"diaper\" [\"hello\", \"world\", \"zombies\", \"pants\"]\n Test.label \"no matches\" <| Test.expect (actual === [])"
5+
},
6+
{
7+
"name": "detects two anagrams",
8+
"test_code": "anagram.findAnagrams.tests.ex2 = let\n actual = findAnagrams \"solemn\" [\"lemons\",\"cherry\",\"melons\"]\n expected = [\"lemons\",\"melons\"]\n Test.label \"detects two anagrams\" <| Test.expect (actual === expected)"
9+
},
10+
{
11+
"name": "does not detect anagram subsets",
12+
"test_code": "anagram.findAnagrams.tests.ex3 = let\n actual = findAnagrams \"good\" [\"dog\",\"goody\"]\n expected = []\n Test.label \"does not detect anagram subsets\" <| Test.expect (actual === expected)"
13+
},
14+
{
15+
"name": "detects anagram",
16+
"test_code": "anagram.findAnagrams.tests.ex4 = let\n actual = findAnagrams \"listen\" [\"enlists\",\"google\",\"inlets\",\"banana\"]\n expected = [\"inlets\"]\n Test.label \"detects anagram\" <| Test.expect (actual === expected)"
17+
},
18+
{
19+
"name": "detects three anagrams",
20+
"test_code": "anagram.findAnagrams.tests.ex5 = let\n actual = findAnagrams \"allergy\" [\"gallery\",\"ballerina\",\"regally\",\"clergy\",\"largely\",\"leading\"]\n expected = [\"gallery\",\"regally\",\"largely\"]\n Test.label \"detects three anagrams\" <| Test.expect (actual === expected)"
21+
},
22+
{
23+
"name": "detects multiple anagrams with different case",
24+
"test_code": "anagram.findAnagrams.tests.ex6 = let\n actual = findAnagrams \"nose\" [\"Eons\",\"ONES\"]\n expected = [\"Eons\",\"ONES\"]\n Test.label \"detects multiple anagrams with different case\" <| Test.expect (actual === expected)"
25+
},
26+
{
27+
"name": "does not detect non-anagrams with identical checksum",
28+
"test_code": "anagram.findAnagrams.tests.ex7 = let\n actual = findAnagrams \"mass\" [\"last\"]\n expected = []\n Test.label \"does not detect non-anagrams with identical checksum\" <| Test.expect (actual === expected)"
29+
},
30+
{
31+
"name": "detects anagrams case-insensitively",
32+
"test_code": "anagram.findAnagrams.tests.ex8 = let\n actual = findAnagrams \"Orchestra\" [\"cashregister\",\"Carthorse\",\"radishes\"]\n expected = [\"Carthorse\"]\n Test.label \"detects anagrams case-insensitively\" <| Test.expect (actual === expected)"
33+
},
34+
{
35+
"name": "detects anagrams using case-insensitive subject",
36+
"test_code": "anagram.findAnagrams.tests.ex9 = let\n actual = findAnagrams \"Orchestra\" [\"cashregister\",\"carthorse\",\"radishes\"]\n expected = [\"carthorse\"]\n Test.label \"detects anagrams using case-insensitive subject\" <| Test.expect (actual === expected)"
37+
},
38+
{
39+
"name": "detects anagrams using case-insensitive possible matches",
40+
"test_code": "anagram.findAnagrams.tests.ex10 = let\n actual = findAnagrams \"orchestra\" [\"cashregister\",\"Carthorse\",\"radishes\"]\n expected = [\"Carthorse\"]\n Test.label \"detects anagrams using case-insensitive possible matches\" <| Test.expect (actual === expected)"
41+
},
42+
{
43+
"name": "does not detect an anagram if the original word is repeated",
44+
"test_code": "anagram.findAnagrams.tests.ex11 = let \n actual = findAnagrams \"go\" [\"go Go GO\"]\n expected = []\n Test.label \"does not detect an anagram if the original word is repeated\" <| Test.expect (actual === expected)"
45+
},
46+
{
47+
"name": "anagrams must use all letters exactly once",
48+
"test_code": "anagram.findAnagrams.tests.ex12 = let\n actual = findAnagrams \"tapper\" [\"patter\"]\n expected = []\n Test.label \"anagrams must use all letters exactly once\" <| Test.expect (actual === expected)"
49+
},
50+
{
51+
"name": "words are not anagrams of themselves",
52+
"test_code": "anagram.findAnagrams.tests.ex13 = let\n actual = findAnagrams \"BANANA\" [\"BANANA\"]\n expected = []\n Test.label \"words are not anagrams of themselves\" <| Test.expect (actual === expected)"
53+
},
54+
{
55+
"name": "words are not anagrams of themselves even if letter case is partially different",
56+
"test_code": "anagram.findAnagrams.tests.ex14 = let\n actual = findAnagrams \"BANANA\" [\"Banana\"]\n expected = []\n Test.label \"words are not anagrams of themselves even if letter case is partially different\" <| Test.expect (actual === expected)"
57+
},
58+
{
59+
"name": "words are not anagrams of themselves even if letter case is completely different",
60+
"test_code": "anagram.findAnagrams.tests.ex15 = let\n actual = findAnagrams \"BANANA\" [\"banana\"]\n expected = []\n Test.label \"words are not anagrams of themselves even if letter case is completely different\" <| Test.expect (actual === expected)"
61+
},
62+
{
63+
"name": "words other than themselves can be anagrams",
64+
"test_code": "anagram.findAnagrams.tests.ex16 = let\n actual = findAnagrams \"LISTEN\" [\"LISTEN\",\"Silent\" ]\n expected = [\"Silent\"]\n Test.label \"words other than themselves can be anagrams\" <| Test.expect (actual === expected)"
65+
}
66+
]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
# Testing transcript for anagram exercise
2+
3+
```ucm
4+
.> load ./anagram.u
5+
.> add
6+
.> load ./anagram.test.u
7+
.> add
8+
.> move.term anagram.tests tests
9+
```
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
# This is an auto-generated file.
2+
#
3+
# Regenerating this file via `configlet sync` will:
4+
# - Recreate every `description` key/value pair
5+
# - Recreate every `reimplements` key/value pair, where they exist in problem-specifications
6+
# - Remove any `include = true` key/value pair (an omitted `include` key implies inclusion)
7+
# - Preserve any other key/value pair
8+
#
9+
# As user-added comments (using the # character) will be removed when this file
10+
# is regenerated, comments can be added via a `comment` key.
11+
12+
[dd40c4d2-3c8b-44e5-992a-f42b393ec373]
13+
description = "no matches"
14+
15+
[b3cca662-f50a-489e-ae10-ab8290a09bdc]
16+
description = "detects two anagrams"
17+
include = false
18+
19+
[03eb9bbe-8906-4ea0-84fa-ffe711b52c8b]
20+
description = "detects two anagrams"
21+
reimplements = "b3cca662-f50a-489e-ae10-ab8290a09bdc"
22+
23+
[a27558ee-9ba0-4552-96b1-ecf665b06556]
24+
description = "does not detect anagram subsets"
25+
26+
[64cd4584-fc15-4781-b633-3d814c4941a4]
27+
description = "detects anagram"
28+
29+
[99c91beb-838f-4ccd-b123-935139917283]
30+
description = "detects three anagrams"
31+
32+
[78487770-e258-4e1f-a646-8ece10950d90]
33+
description = "detects multiple anagrams with different case"
34+
35+
[1d0ab8aa-362f-49b7-9902-3d0c668d557b]
36+
description = "does not detect non-anagrams with identical checksum"
37+
38+
[9e632c0b-c0b1-4804-8cc1-e295dea6d8a8]
39+
description = "detects anagrams case-insensitively"
40+
41+
[b248e49f-0905-48d2-9c8d-bd02d8c3e392]
42+
description = "detects anagrams using case-insensitive subject"
43+
44+
[f367325c-78ec-411c-be76-e79047f4bd54]
45+
description = "detects anagrams using case-insensitive possible matches"
46+
47+
[7cc195ad-e3c7-44ee-9fd2-d3c344806a2c]
48+
description = "does not detect an anagram if the original word is repeated"
49+
include = false
50+
51+
[630abb71-a94e-4715-8395-179ec1df9f91]
52+
description = "does not detect an anagram if the original word is repeated"
53+
reimplements = "7cc195ad-e3c7-44ee-9fd2-d3c344806a2c"
54+
55+
[9878a1c9-d6ea-4235-ae51-3ea2befd6842]
56+
description = "anagrams must use all letters exactly once"
57+
58+
[85757361-4535-45fd-ac0e-3810d40debc1]
59+
description = "words are not anagrams of themselves (case-insensitive)"
60+
include = false
61+
62+
[68934ed0-010b-4ef9-857a-20c9012d1ebf]
63+
description = "words are not anagrams of themselves"
64+
reimplements = "85757361-4535-45fd-ac0e-3810d40debc1"
65+
66+
[589384f3-4c8a-4e7d-9edc-51c3e5f0c90e]
67+
description = "words are not anagrams of themselves even if letter case is partially different"
68+
reimplements = "85757361-4535-45fd-ac0e-3810d40debc1"
69+
70+
[ba53e423-7e02-41ee-9ae2-71f91e6d18e6]
71+
description = "words are not anagrams of themselves even if letter case is completely different"
72+
reimplements = "85757361-4535-45fd-ac0e-3810d40debc1"
73+
74+
[a0705568-628c-4b55-9798-82e4acde51ca]
75+
description = "words other than themselves can be anagrams"
76+
include = false
77+
78+
[33d3f67e-fbb9-49d3-a90e-0beb00861da7]
79+
description = "words other than themselves can be anagrams"
80+
reimplements = "a0705568-628c-4b55-9798-82e4acde51ca"
81+
82+
[a6854f66-eec1-4afd-a137-62ef2870c051]
83+
description = "handles case of greek letters"
84+
85+
[fd3509e5-e3ba-409d-ac3d-a9ac84d13296]
86+
description = "different characters may have the same bytes"
+97
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
anagram.findAnagrams.tests.ex1 = let
2+
actual = findAnagrams "diaper" ["hello", "world", "zombies", "pants"]
3+
Test.label "no matches" <| Test.expect (actual === [])
4+
5+
anagram.findAnagrams.tests.ex2 = let
6+
actual = findAnagrams "solemn" ["lemons","cherry","melons"]
7+
expected = ["lemons","melons"]
8+
Test.label "detects two anagrams" <| Test.expect (actual === expected)
9+
10+
anagram.findAnagrams.tests.ex3 = let
11+
actual = findAnagrams "good" ["dog","goody"]
12+
expected = []
13+
Test.label "does not detect anagram subsets" <| Test.expect (actual === expected)
14+
15+
anagram.findAnagrams.tests.ex4 = let
16+
actual = findAnagrams "listen" ["enlists","google","inlets","banana"]
17+
expected = ["inlets"]
18+
Test.label "detects anagram" <| Test.expect (actual === expected)
19+
20+
anagram.findAnagrams.tests.ex5 = let
21+
actual = findAnagrams "allergy" ["gallery","ballerina","regally","clergy","largely","leading"]
22+
expected = ["gallery","regally","largely"]
23+
Test.label "detects three anagrams" <| Test.expect (actual === expected)
24+
25+
anagram.findAnagrams.tests.ex6 = let
26+
actual = findAnagrams "nose" ["Eons","ONES"]
27+
expected = ["Eons","ONES"]
28+
Test.label "detects multiple anagrams with different case" <| Test.expect (actual === expected)
29+
30+
anagram.findAnagrams.tests.ex7 = let
31+
actual = findAnagrams "mass" ["last"]
32+
expected = []
33+
Test.label "does not detect non-anagrams with identical checksum" <| Test.expect (actual === expected)
34+
35+
anagram.findAnagrams.tests.ex8 = let
36+
actual = findAnagrams "Orchestra" ["cashregister","Carthorse","radishes"]
37+
expected = ["Carthorse"]
38+
Test.label "detects anagrams case-insensitively" <| Test.expect (actual === expected)
39+
40+
anagram.findAnagrams.tests.ex9 = let
41+
actual = findAnagrams "Orchestra" ["cashregister","carthorse","radishes"]
42+
expected = ["carthorse"]
43+
Test.label "detects anagrams using case-insensitive subject" <| Test.expect (actual === expected)
44+
45+
anagram.findAnagrams.tests.ex10 = let
46+
actual = findAnagrams "orchestra" ["cashregister","Carthorse","radishes"]
47+
expected = ["Carthorse"]
48+
Test.label "detects anagrams using case-insensitive possible matches" <| Test.expect (actual === expected)
49+
50+
anagram.findAnagrams.tests.ex11 = let
51+
actual = findAnagrams "go" ["go Go GO"]
52+
expected = []
53+
Test.label "does not detect an anagram if the original word is repeated" <| Test.expect (actual === expected)
54+
55+
anagram.findAnagrams.tests.ex12 = let
56+
actual = findAnagrams "tapper" ["patter"]
57+
expected = []
58+
Test.label "anagrams must use all letters exactly once" <| Test.expect (actual === expected)
59+
60+
anagram.findAnagrams.tests.ex13 = let
61+
actual = findAnagrams "BANANA" ["BANANA"]
62+
expected = []
63+
Test.label "words are not anagrams of themselves" <| Test.expect (actual === expected)
64+
65+
anagram.findAnagrams.tests.ex14 = let
66+
actual = findAnagrams "BANANA" ["Banana"]
67+
expected = []
68+
Test.label "words are not anagrams of themselves even if letter case is partially different" <| Test.expect (actual === expected)
69+
70+
anagram.findAnagrams.tests.ex15 = let
71+
actual = findAnagrams "BANANA" ["banana"]
72+
expected = []
73+
Test.label "words are not anagrams of themselves even if letter case is completely different" <| Test.expect (actual === expected)
74+
75+
anagram.findAnagrams.tests.ex16 = let
76+
actual = findAnagrams "LISTEN" ["LISTEN","Silent" ]
77+
expected = ["Silent"]
78+
Test.label "words other than themselves can be anagrams" <| Test.expect (actual === expected)
79+
80+
test> anagram.tests = runAll [
81+
anagram.findAnagrams.tests.ex1,
82+
anagram.findAnagrams.tests.ex2,
83+
anagram.findAnagrams.tests.ex3,
84+
anagram.findAnagrams.tests.ex4,
85+
anagram.findAnagrams.tests.ex5,
86+
anagram.findAnagrams.tests.ex6,
87+
anagram.findAnagrams.tests.ex7,
88+
anagram.findAnagrams.tests.ex8,
89+
anagram.findAnagrams.tests.ex9,
90+
anagram.findAnagrams.tests.ex10,
91+
anagram.findAnagrams.tests.ex11,
92+
anagram.findAnagrams.tests.ex12,
93+
anagram.findAnagrams.tests.ex13,
94+
anagram.findAnagrams.tests.ex14,
95+
anagram.findAnagrams.tests.ex15,
96+
anagram.findAnagrams.tests.ex16
97+
]

exercises/practice/anagram/anagram.u

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
anagram.findAnagrams : Text -> [Text] -> [Text]
2+
anagram.findAnagrams subject candidates = todo "implement findAnagrams"

0 commit comments

Comments
 (0)