Skip to content

Commit

Permalink
Add say exercise
Browse files Browse the repository at this point in the history
  • Loading branch information
keiravillekode committed Dec 26, 2024
1 parent 1e07632 commit bef7637
Show file tree
Hide file tree
Showing 9 changed files with 431 additions and 1 deletion.
2 changes: 1 addition & 1 deletion bin/generate
Original file line number Diff line number Diff line change
Expand Up @@ -355,7 +355,7 @@ def generate(exercise, flags, problem_specs_source, force=False):
write(path / ('%s.sml' % exercise), content)

if flags & EXAMPLE:
write(path / 'example.sml', content)
write(path / '.meta/example.sml', content)

shutil.copyfile(
(root / 'lib/testlib.sml').as_posix(),
Expand Down
8 changes: 8 additions & 0 deletions config.json
Original file line number Diff line number Diff line change
Expand Up @@ -609,6 +609,14 @@
"prerequisites": [],
"difficulty": 5
},
{
"slug": "say",
"name": "Say",
"uuid": "f3bcc2f2-9752-4fb4-81f0-a790c0c2b3e6",
"practices": [],
"prerequisites": [],
"difficulty": 5
},
{
"slug": "transpose",
"name": "Transpose",
Expand Down
48 changes: 48 additions & 0 deletions exercises/practice/say/.docs/instructions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
# Instructions

Given a number from 0 to 999,999,999,999, spell out that number in English.

## Step 1

Handle the basic case of 0 through 99.

If the input to the program is `22`, then the output should be `'twenty-two'`.

Your program should complain loudly if given a number outside the blessed range.

Some good test cases for this program are:

- 0
- 14
- 50
- 98
- -1
- 100

### Extension

If you're on a Mac, shell out to Mac OS X's `say` program to talk out loud.
If you're on Linux or Windows, eSpeakNG may be available with the command `espeak`.

## Step 2

Implement breaking a number up into chunks of thousands.

So `1234567890` should yield a list like 1, 234, 567, and 890, while the far simpler `1000` should yield just 1 and 0.

## Step 3

Now handle inserting the appropriate scale word between those chunks.

So `1234567890` should yield `'1 billion 234 million 567 thousand 890'`

The program must also report any values that are out of range.
It's fine to stop at "trillion".

## Step 4

Put it all together to get nothing but plain English.

`12345` should give `twelve thousand three hundred forty-five`.

The program must also report any values that are out of range.
19 changes: 19 additions & 0 deletions exercises/practice/say/.meta/config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
"authors": [
"keiravillekode"
],
"files": {
"solution": [
"say.sml"
],
"test": [
"test.sml"
],
"example": [
".meta/example.sml"
]
},
"blurb": "Given a number from 0 to 999,999,999,999, spell out that number in English.",
"source": "A variation on the JavaRanch CattleDrive, Assignment 4",
"source_url": "https://coderanch.com/wiki/718804"
}
59 changes: 59 additions & 0 deletions exercises/practice/say/.meta/example.sml
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
val unitNames = Array.fromList [
"zero",
"one",
"two",
"three",
"four",
"five",
"six",
"seven",
"eight",
"nine",
"ten",
"eleven",
"twelve",
"thirteen",
"fourteen",
"fifteen",
"sixteen",
"seventeen",
"eighteen",
"nineteen"
]

val decadeNames = Array.fromList [
"zero",
"ten",
"twenty",
"thirty",
"forty",
"fifty",
"sixty",
"seventy",
"eighty",
"ninety"
]

fun words (number: int): string list =
if number >= 1000000000
then (words (number div 1000000000)) @ ("billion" :: (words (number mod 1000000000)))
else if number >= 1000000
then (words (number div 1000000)) @ ("million" :: (words (number mod 1000000)))
else if number >= 1000
then (words (number div 1000)) @ ("thousand" :: (words (number mod 1000)))
else if number >= 100
then (words (number div 100)) @ ("hundred" :: (words (number mod 100)))
else if number = 0
then []
else if number < 20
then [Array.sub (unitNames, number)]
else if (number mod 10) = 0
then [Array.sub (decadeNames, number div 10)]
else [Array.sub (decadeNames, number div 10) ^ "-" ^ Array.sub (unitNames, number mod 10)]

fun say (number: int): string =
if (number < 0) orelse (number > 999999999999)
then raise Fail "input out of range"
else if number = 0
then "zero"
else String.concatWith " " (words number)
67 changes: 67 additions & 0 deletions exercises/practice/say/.meta/tests.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
# 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.

[5d22a120-ba0c-428c-bd25-8682235d83e8]
description = "zero"

[9b5eed77-dbf6-439d-b920-3f7eb58928f6]
description = "one"

[7c499be1-612e-4096-a5e1-43b2f719406d]
description = "fourteen"

[f541dd8e-f070-4329-92b4-b7ce2fcf06b4]
description = "twenty"

[d78601eb-4a84-4bfa-bf0e-665aeb8abe94]
description = "twenty-two"

[f010d4ca-12c9-44e9-803a-27789841adb1]
description = "thirty"

[738ce12d-ee5c-4dfb-ad26-534753a98327]
description = "ninety-nine"

[e417d452-129e-4056-bd5b-6eb1df334dce]
description = "one hundred"

[d6924f30-80ba-4597-acf6-ea3f16269da8]
description = "one hundred twenty-three"

[2f061132-54bc-4fd4-b5df-0a3b778959b9]
description = "two hundred"

[feed6627-5387-4d38-9692-87c0dbc55c33]
description = "nine hundred ninety-nine"

[3d83da89-a372-46d3-b10d-de0c792432b3]
description = "one thousand"

[865af898-1d5b-495f-8ff0-2f06d3c73709]
description = "one thousand two hundred thirty-four"

[b6a3f442-266e-47a3-835d-7f8a35f6cf7f]
description = "one million"

[2cea9303-e77e-4212-b8ff-c39f1978fc70]
description = "one million two thousand three hundred forty-five"

[3e240eeb-f564-4b80-9421-db123f66a38f]
description = "one billion"

[9a43fed1-c875-4710-8286-5065d73b8a9e]
description = "a big number"

[49a6a17b-084e-423e-994d-a87c0ecc05ef]
description = "numbers below zero are out of range"

[4d6492eb-5853-4d16-9d34-b0f61b261fd9]
description = "numbers above 999,999,999,999 are out of range"
2 changes: 2 additions & 0 deletions exercises/practice/say/say.sml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
fun say (number: int): string =
raise Fail "'say' is not implemented"
67 changes: 67 additions & 0 deletions exercises/practice/say/test.sml
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
use "testlib.sml";
use "say.sml";

infixr |>
fun x |> f = f x

val testsuite =
describe "say" [
test "zero"
(fn _ => say 0 |> Expect.equalTo "zero"),

test "one"
(fn _ => say 1 |> Expect.equalTo "one"),

test "fourteen"
(fn _ => say 14 |> Expect.equalTo "fourteen"),

test "twenty"
(fn _ => say 20 |> Expect.equalTo "twenty"),

test "twenty-two"
(fn _ => say 22 |> Expect.equalTo "twenty-two"),

test "thirty"
(fn _ => say 30 |> Expect.equalTo "thirty"),

test "ninety-nine"
(fn _ => say 99 |> Expect.equalTo "ninety-nine"),

test "one hundred"
(fn _ => say 100 |> Expect.equalTo "one hundred"),

test "one hundred twenty-three"
(fn _ => say 123 |> Expect.equalTo "one hundred twenty-three"),

test "two hundred"
(fn _ => say 200 |> Expect.equalTo "two hundred"),

test "nine hundred ninety-nine"
(fn _ => say 999 |> Expect.equalTo "nine hundred ninety-nine"),

test "one thousand"
(fn _ => say 1000 |> Expect.equalTo "one thousand"),

test "one thousand two hundred thirty-four"
(fn _ => say 1234 |> Expect.equalTo "one thousand two hundred thirty-four"),

test "one million"
(fn _ => say 1000000 |> Expect.equalTo "one million"),

test "one million two thousand three hundred forty-five"
(fn _ => say 1002345 |> Expect.equalTo "one million two thousand three hundred forty-five"),

test "one billion"
(fn _ => say 1000000000 |> Expect.equalTo "one billion"),

test "a big number"
(fn _ => say 987654321123 |> Expect.equalTo "nine hundred eighty-seven billion six hundred fifty-four million three hundred twenty-one thousand one hundred twenty-three"),

test "numbers below zero are out of range"
(fn _ => (fn _ => say ~1) |> Expect.error (Fail "input out of range")),

test "numbers above 999,999,999,999 are out of range"
(fn _ => (fn _ => say 1000000000000) |> Expect.error (Fail "input out of range"))
]

val _ = Test.run testsuite
Loading

0 comments on commit bef7637

Please sign in to comment.