Add anagram exercise (#181)

Co-authored-by: Glenn Jackman <glenn.jackman@gmail.com>
This commit is contained in:
Erik Schierboom
2024-05-11 16:56:57 +02:00
committed by GitHub
parent 5ccfb5abaa
commit 5e703a8706
8 changed files with 405 additions and 0 deletions

View File

@@ -485,6 +485,14 @@
"prerequisites": [],
"difficulty": 2
},
{
"slug": "anagram",
"name": "Anagram",
"uuid": "3d67ba9d-0a6c-4a30-9493-3c6b6c2693e1",
"practices": [],
"prerequisites": [],
"difficulty": 3
},
{
"slug": "spiral-matrix",
"name": "Spiral Matrix",

View File

@@ -0,0 +1,13 @@
# Instructions
An anagram is a rearrangement of letters to form a new word: for example `"owns"` is an anagram of `"snow"`.
A word is not its own anagram: for example, `"stop"` is not an anagram of `"stop"`.
Given a target word and a set of candidate words, this exercise requests the anagram set: the subset of the candidates that are anagrams of the target.
The target and candidates are words of one or more ASCII alphabetic characters (`A`-`Z` and `a`-`z`).
Lowercase and uppercase characters are equivalent: for example, `"PoTS"` is an anagram of `"sTOp"`, but `StoP` is not an anagram of `sTOp`.
The anagram set is the subset of the candidate set that are anagrams of the target (in any order).
Words in the anagram set should have the same letter case as in the candidate set.
Given the target `"stone"` and candidates `"stone"`, `"tones"`, `"banana"`, `"tons"`, `"notes"`, `"Seton"`, the anagram set is `"tones"`, `"notes"`, `"Seton"`.

View File

@@ -0,0 +1,19 @@
{
"authors": [
"erikschierboom"
],
"files": {
"solution": [
"anagram.8th"
],
"test": [
"test.8th"
],
"example": [
".meta/example.8th"
]
},
"blurb": "Given a word and a list of possible anagrams, select the correct sublist.",
"source": "Inspired by the Extreme Startup game",
"source_url": "https://github.com/rchatley/extreme_startup"
}

View File

@@ -0,0 +1,12 @@
: normalize \ s -- a
s:lc null s:/ ' s:cmp a:sort
;
: anagram? \ s s -- T
2dup s:=ic if 2drop false ;then
normalize swap normalize ' s:= a:= 2nip
;
: anagrams \ s a -- a
( over anagram? ) a:filter nip
;

View File

@@ -0,0 +1,88 @@
# This is an auto-generated file.
#
# Regenerating this file via `configlet sync` will:
# - Recreate every `description` key/value pair
# - Recreate every `reimplements` key/value pair, where they exist in problem-specifications
# - Remove any `include = true` key/value pair (an omitted `include` key implies inclusion)
# - Preserve any other key/value pair
#
# As user-added comments (using the # character) will be removed when this file
# is regenerated, comments can be added via a `comment` key.
[dd40c4d2-3c8b-44e5-992a-f42b393ec373]
description = "no matches"
[b3cca662-f50a-489e-ae10-ab8290a09bdc]
description = "detects two anagrams"
include = false
[03eb9bbe-8906-4ea0-84fa-ffe711b52c8b]
description = "detects two anagrams"
reimplements = "b3cca662-f50a-489e-ae10-ab8290a09bdc"
[a27558ee-9ba0-4552-96b1-ecf665b06556]
description = "does not detect anagram subsets"
[64cd4584-fc15-4781-b633-3d814c4941a4]
description = "detects anagram"
[99c91beb-838f-4ccd-b123-935139917283]
description = "detects three anagrams"
[78487770-e258-4e1f-a646-8ece10950d90]
description = "detects multiple anagrams with different case"
[1d0ab8aa-362f-49b7-9902-3d0c668d557b]
description = "does not detect non-anagrams with identical checksum"
[9e632c0b-c0b1-4804-8cc1-e295dea6d8a8]
description = "detects anagrams case-insensitively"
[b248e49f-0905-48d2-9c8d-bd02d8c3e392]
description = "detects anagrams using case-insensitive subject"
[f367325c-78ec-411c-be76-e79047f4bd54]
description = "detects anagrams using case-insensitive possible matches"
[7cc195ad-e3c7-44ee-9fd2-d3c344806a2c]
description = "does not detect an anagram if the original word is repeated"
include = false
[630abb71-a94e-4715-8395-179ec1df9f91]
description = "does not detect an anagram if the original word is repeated"
reimplements = "7cc195ad-e3c7-44ee-9fd2-d3c344806a2c"
[9878a1c9-d6ea-4235-ae51-3ea2befd6842]
description = "anagrams must use all letters exactly once"
[85757361-4535-45fd-ac0e-3810d40debc1]
description = "words are not anagrams of themselves (case-insensitive)"
include = false
[68934ed0-010b-4ef9-857a-20c9012d1ebf]
description = "words are not anagrams of themselves"
reimplements = "85757361-4535-45fd-ac0e-3810d40debc1"
[589384f3-4c8a-4e7d-9edc-51c3e5f0c90e]
description = "words are not anagrams of themselves even if letter case is partially different"
reimplements = "85757361-4535-45fd-ac0e-3810d40debc1"
[ba53e423-7e02-41ee-9ae2-71f91e6d18e6]
description = "words are not anagrams of themselves even if letter case is completely different"
reimplements = "85757361-4535-45fd-ac0e-3810d40debc1"
[a0705568-628c-4b55-9798-82e4acde51ca]
description = "words other than themselves can be anagrams"
include = false
[33d3f67e-fbb9-49d3-a90e-0beb00861da7]
description = "words other than themselves can be anagrams"
reimplements = "a0705568-628c-4b55-9798-82e4acde51ca"
[a6854f66-eec1-4afd-a137-62ef2870c051]
description = "handles case of greek letters"
include = false
[fd3509e5-e3ba-409d-ac3d-a9ac84d13296]
description = "different characters may have the same bytes"
include = false

View File

@@ -0,0 +1,3 @@
: anagrams \ s a -- a
;

View File

@@ -0,0 +1,173 @@
needs console/loaded
\ -----------------------------------------------------------------
ns: test
-1 var, test-count
var tests-passed
var tests-failed
var tests-skipped
true var, run-test
\ Some utility words
: test-passed \ s x x -- \\ test name, expected value, actual value
2drop
1 tests-passed n:+!
con:green con:onBlack . space " ... OK" . con:white con:onBlack cr
;
: test-skipped \ s --
1 tests-skipped n:+!
con:cyan con:onBlack . space " ... SKIPPED" . con:white con:onBlack cr
;
: test-failed \ s x x -- \\ test name, expected value, actual value
1 tests-failed n:+!
rot
con:red con:onBlack . space " ... FAIL" . con:white con:onBlack cr
" Actual: «" . . "»" . cr
" Expected: «" . . "»" . cr cr
;
: isword? \ x -- x f
dup >kind ns:w n:=
;
: run-test? \ -- T
run-test @ if true else "RUN_ALL_TESTS" getenv n:>bool then
;
\ Num passed + num skipped + num failed should == num tests
: all-tests-run? \ -- T
tests-passed @ tests-skipped @ tests-failed @ n:+ n:+
test-count @ n:=
;
\ returns true if x is a date, false otherwise
: date? \ x -- x T
dup >kind ns:d n:=
;
\ adapted from 8th forum -- https://8th-dev.com/forum/index.php/topic,2745.0.html
: eq? \ x x -- T
\ are the items the same kind?
2dup >kind swap >kind n:=
!if 2drop false ;then
\ same kind: try different comparators
number? if n:= ;then
string? if s:= ;then
array? if ' eq? a:= 2nip ;then
map? if ' eq? m:= 2nip ;then
date? if d:= ;then
\ otherwise fall back to 'lazy evaluation'
l: =
;
: eps_eq? \ n x x -- T
\ are the items the same kind?
2dup >kind swap >kind n:=
!if 2drop false ;then
number? !if 2drop false ;then
rot n:~=
;
: check-depth \ ... n -- ...
dup>r
n:1+ depth n:=
!if
con:red con:onBlack
"PANIC: expected stack depth to be " . r> . cr
"Stack is:" . cr
.s cr
255 die
then
rdrop
;
\ -----------------------------------------------------------------
\ status report at end of run
( all-tests-run?
!if con:red con:onBlack "... FAIL - not all tests completed" . con:white con:onBlack cr then
) onexit
\ Print a summary of the tests run
( con:white con:onBlack
test-count @ . space "tests planned - " .
tests-passed @ . space "passed - " .
tests-skipped @ . space "skipped - " .
tests-failed @ . space "failed" . cr
) onexit
\ -----------------------------------------------------------------
\ The public-facing words
\ -----------------------------------------------------------------
: equal? \ s x w -- | s w x --
run-test? !if 2drop test-skipped ;; then
isword? !if swap then
w:exec
3 check-depth
2dup \ so test-failed can show actual and expected
eq? if test-passed else test-failed then
;
: approx_equal? \ s x w n -- | s w x n --
run-test? !if 3drop test-skipped ;; then
-rot isword? !if swap then
w:exec
4 check-depth
3dup \ so test-failed can show actual and expected
eps_eq?
if rot drop test-passed else rot drop test-failed then
;
: true? \ s w --
run-test? !if drop test-skipped ;; then
w:exec
2 check-depth
true swap dup \ so test-failed can show actual and expected
if test-passed else test-failed then
;
: false? \ s w --
run-test? !if drop test-skipped ;; then
w:exec
2 check-depth
false swap dup \ so test-failed can show actual and expected
!if test-passed else test-failed then
;
: null? \ s w --
run-test? !if drop test-skipped ;; then
w:exec
2 check-depth
null swap dup \ so test-failed can show actual and expected
G:null? nip if test-passed else test-failed then
;
: SKIP-REST-OF-TESTS false run-test ! ;
: tests \ n --
test-count !
;
\ Set the exit status:
\ 0 = all OK
\ 1 = not all tests were run (some error occurred)
\ 2 = some tests failed
: end-of-tests \ --
all-tests-run?
if
tests-failed @ 0 n:= if 0 else 2 then
else
1
then
die
;

View File

@@ -0,0 +1,89 @@
"anagram.8th" f:include
needs exercism/test
with: test
16 tests
"no matches"
( "diaper" ["hello", "world", "zombies", "pants"] anagrams )
[]
equal?
SKIP-REST-OF-TESTS
"detects two anagrams"
( "solemn" ["lemons", "cherry", "melons"] anagrams )
["lemons", "melons"]
equal?
"does not detect anagram subsets"
( "good" ["dog", "goody"] anagrams )
[]
equal?
"detects anagram"
( "listen" ["enlists", "google", "inlets", "banana"] anagrams )
["inlets"]
equal?
"detects three anagrams"
( "allergy" ["gallery", "ballerina", "regally", "clergy", "largely", "leading"] anagrams )
["gallery", "regally", "largely"]
equal?
"detects multiple anagrams with different case"
( "nose" ["Eons", "ONES"] anagrams )
["Eons", "ONES"]
equal?
"does not detect non-anagrams with identical checksum"
( "mass" ["last"] anagrams )
[]
equal?
"detects anagrams case-insensitively"
( "Orchestra" ["cashregister", "Carthorse", "radishes"] anagrams )
["Carthorse"]
equal?
"detects anagrams using case-insensitive subject"
( "Orchestra" ["cashregister", "carthorse", "radishes"] anagrams )
["carthorse"]
equal?
"detects anagrams using case-insensitive possible matches"
( "orchestra" ["cashregister", "Carthorse", "radishes"] anagrams )
["Carthorse"]
equal?
"does not detect an anagram if the original word is repeated"
( "go" ["goGoGO"] anagrams )
[]
equal?
"anagrams must use all letters exactly once"
( "tapper" ["patter"] anagrams )
[]
equal?
"words are not anagrams of themselves"
( "BANANA" ["BANANA"] anagrams )
[]
equal?
"words are not anagrams of themselves even if letter case is partially different"
( "BANANA" ["Banana"] anagrams )
[]
equal?
"words are not anagrams of themselves even if letter case is completely different"
( "BANANA" ["banana"] anagrams )
[]
equal?
"words other than themselves can be anagrams"
( "LISTEN" ["LISTEN", "Silent"] anagrams )
["Silent"]
equal?
end-of-tests
;with