Skip to content

Commit

Permalink
Add say exercise (#106)
Browse files Browse the repository at this point in the history
  • Loading branch information
ageron authored Sep 22, 2024
1 parent b381b69 commit aa2e596
Show file tree
Hide file tree
Showing 8 changed files with 335 additions and 0 deletions.
8 changes: 8 additions & 0 deletions config.json
Original file line number Diff line number Diff line change
Expand Up @@ -327,6 +327,14 @@
"prerequisites": [],
"difficulty": 3
},
{
"slug": "say",
"name": "Say",
"uuid": "23345ef2-4b3c-4c67-91d0-58bc472fc22b",
"practices": [],
"prerequisites": [],
"difficulty": 3
},
{
"slug": "sieve",
"name": "Sieve",
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.
68 changes: 68 additions & 0 deletions exercises/practice/say/.meta/Example.roc
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
module [say]

zeroToNineteen = [
"zero",
"one",
"two",
"three",
"four",
"five",
"six",
"seven",
"eight",
"nine",
"ten",
"eleven",
"twelve",
"thirteen",
"fourteen",
"fifteen",
"sixteen",
"seventeen",
"eighteen",
"nineteen",
]

tensAfterTen = [
"twenty",
"thirty",
"forty",
"fifty",
"sixty",
"seventy",
"eighty",
"ninety",
]

say : U64 -> Result Str [OutOfBounds]
say = \number ->
if number < 20 then
zeroToNineteen |> List.get? number |> Ok
else if number < 100 then
tensWord = tensAfterTen |> List.get? (number // 10 - 2)
digit = number % 10
if digit > 0 then
digitWord = say? digit
Ok "$(tensWord)-$(digitWord)"
else
Ok tensWord
else if number < 1_000_000_000_000 then
[
(1_000_000_000_000, 1_000_000_000, "billion"),
(1_000_000_000, 1_000_000, "million"),
(1_000_000, 1000, "thousand"),
(1000, 100, "hundred"),
(100, 1, ""),
]
|> List.keepOks \(modulo, divisor, name) ->
howMany = (number % modulo) // divisor
if howMany == 0 then
Err NothingToSay
else
sayHowMany = say? howMany
Ok "$(sayHowMany) $(name)"
|> Str.joinWith " "
|> Str.trimEnd
|> Ok
else
Err OutOfBounds
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": [
"ageron"
],
"files": {
"solution": [
"Say.roc"
],
"test": [
"say-test.roc"
],
"example": [
".meta/Example.roc"
]
},
"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"
}
17 changes: 17 additions & 0 deletions exercises/practice/say/.meta/template.j2
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{%- import "generator_macros.j2" as macros with context -%}
{{ macros.canonical_ref() }}
{{ macros.header() }}

import {{ exercise | to_pascal }} exposing [{{ cases[0]["property"] | to_camel }}]

{% for case in cases -%}
# {{ case["description"] }}
expect
result = {{ case["property"] | to_camel }} {{ case["input"]["number"] | to_roc }}
{%- if case["expected"]["error"] %}
result |> Result.isErr
{%- else %}
result == Ok {{ case["expected"] | to_roc }}
{% endif %}

{% endfor %}
68 changes: 68 additions & 0 deletions exercises/practice/say/.meta/tests.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
# 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"
include = false

[4d6492eb-5853-4d16-9d34-b0f61b261fd9]
description = "numbers above 999,999,999,999 are out of range"
5 changes: 5 additions & 0 deletions exercises/practice/say/Say.roc
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
module [say]

say : U64 -> Result Str [OutOfBounds]
say = \number ->
crash "Please implement the 'say' function"
102 changes: 102 additions & 0 deletions exercises/practice/say/say-test.roc
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
# These tests are auto-generated with test data from:
# https://github.com/exercism/problem-specifications/tree/main/exercises/say/canonical-data.json
# File last updated on 2024-09-22
app [main] {
pf: platform "https://github.com/roc-lang/basic-cli/releases/download/0.15.0/SlwdbJ-3GR7uBWQo6zlmYWNYOxnvo8r6YABXD-45UOw.tar.br",
}

main =
Task.ok {}

import Say exposing [say]

# zero
expect
result = say 0
result == Ok "zero"

# one
expect
result = say 1
result == Ok "one"

# fourteen
expect
result = say 14
result == Ok "fourteen"

# twenty
expect
result = say 20
result == Ok "twenty"

# twenty-two
expect
result = say 22
result == Ok "twenty-two"

# thirty
expect
result = say 30
result == Ok "thirty"

# ninety-nine
expect
result = say 99
result == Ok "ninety-nine"

# one hundred
expect
result = say 100
result == Ok "one hundred"

# one hundred twenty-three
expect
result = say 123
result == Ok "one hundred twenty-three"

# two hundred
expect
result = say 200
result == Ok "two hundred"

# nine hundred ninety-nine
expect
result = say 999
result == Ok "nine hundred ninety-nine"

# one thousand
expect
result = say 1000
result == Ok "one thousand"

# one thousand two hundred thirty-four
expect
result = say 1234
result == Ok "one thousand two hundred thirty-four"

# one million
expect
result = say 1000000
result == Ok "one million"

# one million two thousand three hundred forty-five
expect
result = say 1002345
result == Ok "one million two thousand three hundred forty-five"

# one billion
expect
result = say 1000000000
result == Ok "one billion"

# a big number
expect
result = say 987654321123
result == Ok "nine hundred eighty-seven billion six hundred fifty-four million three hundred twenty-one thousand one hundred twenty-three"

# numbers above 999,999,999,999 are out of range
expect
result = say 1000000000000
result |> Result.isErr

0 comments on commit aa2e596

Please sign in to comment.