Add phone-number exercise (#185)

* Add `kindergarten-garden` exercise (#182)

* Add `phone-number` exercise

* Update exercises/practice/phone-number/.meta/example.8th

Co-authored-by: Glenn Jackman <glenn.jackman@gmail.com>

---------

Co-authored-by: Glenn Jackman <glenn.jackman@gmail.com>
This commit is contained in:
Erik Schierboom
2024-05-15 08:48:35 +02:00
committed by GitHub
parent 079dd18692
commit 62ba261b71
8 changed files with 424 additions and 0 deletions

View File

@@ -516,6 +516,14 @@
"practices": [],
"prerequisites": [],
"difficulty": 5
},
{
"slug": "phone-number",
"name": "Phone Number",
"uuid": "8499b9da-a306-44db-9697-3abc4bd9f4f2",
"practices": [],
"prerequisites": [],
"difficulty": 3
}
]
},

View File

@@ -0,0 +1,34 @@
# Instructions
Clean up user-entered phone numbers so that they can be sent SMS messages.
The **North American Numbering Plan (NANP)** is a telephone numbering system used by many countries in North America like the United States, Canada or Bermuda.
All NANP-countries share the same international country code: `1`.
NANP numbers are ten-digit numbers consisting of a three-digit Numbering Plan Area code, commonly known as _area code_, followed by a seven-digit local number.
The first three digits of the local number represent the _exchange code_, followed by the unique four-digit number which is the _subscriber number_.
The format is usually represented as
```text
NXX NXX-XXXX
```
where `N` is any digit from 2 through 9 and `X` is any digit from 0 through 9.
Sometimes they also have the country code (represented as `1` or `+1`) prefixed.
Your task is to clean up differently formatted telephone numbers by removing punctuation and the country code if present.
For example, the inputs
- `+1 (613)-995-0253`
- `613-995-0253`
- `1 613 995 0253`
- `613.995.0253`
should all produce the output
`6139950253`
**Note:** As this exercise only deals with telephone numbers used in NANP-countries, only 1 is considered a valid country code.

View File

@@ -0,0 +1,19 @@
{
"authors": [
"erikschierboom"
],
"files": {
"solution": [
"phone-number.8th"
],
"test": [
"test.8th"
],
"example": [
".meta/example.8th"
]
},
"blurb": "Clean up user-entered phone numbers so that they can be sent SMS messages.",
"source": "Exercise by the JumpstartLab team for students at The Turing School of Software and Design.",
"source_url": "https://turing.edu"
}

View File

@@ -0,0 +1,17 @@
: remove-punctuation \ s -- s
/[+()\-\s.]/ "" s:replace!
;
: digits \ s -- a
null s:/ ( >n null? if nip break ;then a:push ) a:new a:reduce
;
: clean \ s -- s
remove-punctuation
digits null? if ;then
a:len 11 n:= if a:shift 1 n:= !if drop null ;then then
a:len 10 n:= !if drop null ;then
0 a:@ 2 n:< if drop null ;then
3 a:@ 2 n:< if drop null ;then
' >s a:map "" a:join
;

View File

@@ -0,0 +1,84 @@
# 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.
[79666dce-e0f1-46de-95a1-563802913c35]
description = "cleans the number"
[c360451f-549f-43e4-8aba-fdf6cb0bf83f]
description = "cleans numbers with dots"
[08f94c34-9a37-46a2-a123-2a8e9727395d]
description = "cleans numbers with multiple spaces"
[598d8432-0659-4019-a78b-1c6a73691d21]
description = "invalid when 9 digits"
include = false
[2de74156-f646-42b5-8638-0ef1d8b58bc2]
description = "invalid when 9 digits"
reimplements = "598d8432-0659-4019-a78b-1c6a73691d21"
[57061c72-07b5-431f-9766-d97da7c4399d]
description = "invalid when 11 digits does not start with a 1"
[9962cbf3-97bb-4118-ba9b-38ff49c64430]
description = "valid when 11 digits and starting with 1"
[fa724fbf-054c-4d91-95da-f65ab5b6dbca]
description = "valid when 11 digits and starting with 1 even with punctuation"
[c6a5f007-895a-4fc5-90bc-a7e70f9b5cad]
description = "invalid when more than 11 digits"
include = false
[4a1509b7-8953-4eec-981b-c483358ff531]
description = "invalid when more than 11 digits"
reimplements = "c6a5f007-895a-4fc5-90bc-a7e70f9b5cad"
[63f38f37-53f6-4a5f-bd86-e9b404f10a60]
description = "invalid with letters"
include = false
[eb8a1fc0-64e5-46d3-b0c6-33184208e28a]
description = "invalid with letters"
reimplements = "63f38f37-53f6-4a5f-bd86-e9b404f10a60"
[4bd97d90-52fd-45d3-b0db-06ab95b1244e]
description = "invalid with punctuations"
include = false
[065f6363-8394-4759-b080-e6c8c351dd1f]
description = "invalid with punctuations"
reimplements = "4bd97d90-52fd-45d3-b0db-06ab95b1244e"
[d77d07f8-873c-4b17-8978-5f66139bf7d7]
description = "invalid if area code starts with 0"
[c7485cfb-1e7b-4081-8e96-8cdb3b77f15e]
description = "invalid if area code starts with 1"
[4d622293-6976-413d-b8bf-dd8a94d4e2ac]
description = "invalid if exchange code starts with 0"
[4cef57b4-7d8e-43aa-8328-1e1b89001262]
description = "invalid if exchange code starts with 1"
[9925b09c-1a0d-4960-a197-5d163cbe308c]
description = "invalid if area code starts with 0 on valid 11-digit number"
[3f809d37-40f3-44b5-ad90-535838b1a816]
description = "invalid if area code starts with 1 on valid 11-digit number"
[e08e5532-d621-40d4-b0cc-96c159276b65]
description = "invalid if exchange code starts with 0 on valid 11-digit number"
[57b32f3d-696a-455c-8bf1-137b6d171cdf]
description = "invalid if exchange code starts with 1 on valid 11-digit number"

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,3 @@
: clean \ s -- s
;

View File

@@ -0,0 +1,86 @@
"phone-number.8th" f:include
needs exercism/test
with: test
18 tests
"cleans the number"
( "(223) 456-7890" clean )
"2234567890"
equal?
SKIP-REST-OF-TESTS
"cleans numbers with dots"
( "223.456.7890" clean )
"2234567890"
equal?
"cleans numbers with multiple spaces"
( "223 456 7890 " clean )
"2234567890"
equal?
"invalid when 9 digits"
( "123456789" clean )
null?
"invalid when 11 digits does not start with a 1"
( "22234567890" clean )
null?
"valid when 11 digits and starting with 1"
( "12234567890" clean )
"2234567890"
equal?
"valid when 11 digits and starting with 1 even with punctuation"
( "+1 (223) 456-7890" clean )
"2234567890"
equal?
"invalid when more than 11 digits"
( "321234567890" clean )
null?
"invalid with letters"
( "523-abc-7890" clean )
null?
"invalid with punctuations"
( "523-@:!-7890" clean )
null?
"invalid if area code starts with 0"
( "(023) 456-7890" clean )
null?
"invalid if area code starts with 1"
( "(123) 456-7890" clean )
null?
"invalid if exchange code starts with 0"
( "(223) 056-7890" clean )
null?
"invalid if exchange code starts with 1"
( "(223) 156-7890" clean )
null?
"invalid if area code starts with 0 on valid 11-digit number"
( "1 (023) 456-7890" clean )
null?
"invalid if area code starts with 1 on valid 11-digit number"
( "1 (123) 456-7890" clean )
null?
"invalid if exchange code starts with 0 on valid 11-digit number"
( "1 (223) 056-7890" clean )
null?
"invalid if exchange code starts with 1 on valid 11-digit number"
( "1 (223) 156-7890" clean )
null?
end-of-tests
;with