Skip to content

Commit

Permalink
Add "wordy" exercise
Browse files Browse the repository at this point in the history
  • Loading branch information
SiriusStarr committed Dec 7, 2019
1 parent 4e6a2d3 commit fdded4b
Show file tree
Hide file tree
Showing 6 changed files with 371 additions and 0 deletions.
10 changes: 10 additions & 0 deletions config.json
Original file line number Diff line number Diff line change
Expand Up @@ -501,6 +501,16 @@
"text_formatting",
"transforming"
]
},
{
"slug": "wordy",
"uuid": "7fcc7c1e-c8d4-4701-8be7-06ab6e039885",
"core": false,
"unlocked_by": "robot-simulator",
"difficulty": 8,
"topics": [
"parsing"
]
}
]
}
106 changes: 106 additions & 0 deletions exercises/wordy/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
# Wordy

Parse and evaluate simple math word problems returning the answer as an integer.

## Iteration 0 — Numbers

Problems with no operations simply evaluate to the number given.

> What is 5?
Evaluates to 5.

## Iteration 1 — Addition

Add two numbers together.

> What is 5 plus 13?
Evaluates to 18.

Handle large numbers and negative numbers.

## Iteration 2 — Subtraction, Multiplication and Division

Now, perform the other three operations.

> What is 7 minus 5?
2

> What is 6 multiplied by 4?
24

> What is 25 divided by 5?
5

## Iteration 3 — Multiple Operations

Handle a set of operations, in sequence.

Since these are verbal word problems, evaluate the expression from
left-to-right, _ignoring the typical order of operations._

> What is 5 plus 13 plus 6?
24

> What is 3 plus 2 multiplied by 3?
15 (i.e. not 9)

## Iteration 4 — Errors

The parser should reject:

* Unsupported operations ("What is 52 cubed?")
* Non-math questions ("Who is the President of the United States")
* Word problems with invalid syntax ("What is 1 plus plus 2?")

## Bonus — Exponentials

If you'd like, handle exponentials.

> What is 2 raised to the 5th power?
32

## Hints

This is a perfect opportunity to learn `elm/parser` ([docs](https://package.elm-lang.org/packages/elm/parser/latest/))!

## Elm Installation

Refer to the [Installing Elm](https://exercism.io/tracks/elm/installation) page
for information about installing elm.

## Writing the Code

The code you have to write is located inside the `src/` directory of the exercise.
Elm automatically installs packages dependencies the first time you run the tests
so we can start by running the tests from the exercise directory with:

```bash
$ elm-test
```

To automatically run tests again when you save changes:

```bash
$ elm-test --watch
```

As you work your way through the tests suite in the file `tests/Tests.elm`,
be sure to remove the `skip <|`
calls from each test until you get them all passing!

## Source

Inspired by one of the generated questions in the Extreme Startup game. [https://github.com/rchatley/extreme_startup](https://github.com/rchatley/extreme_startup)

## Submitting Incomplete Solutions

It is possible to submit an incomplete solution so you can see how others have
completed the exercise.
24 changes: 24 additions & 0 deletions exercises/wordy/elm.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
{
"type": "application",
"source-directories": [
"src"
],
"elm-version": "0.19.1",
"dependencies": {
"direct": {
"elm/core": "1.0.0",
"elm/parser": "1.1.0"
},
"indirect": {}
},
"test-dependencies": {
"direct": {
"elm-explorations/test": "1.0.0",
"rtfeldman/elm-iso8601-date-strings": "1.1.3"
},
"indirect": {
"elm/random": "1.0.0",
"elm/json": "1.1.3"
}
}
}
6 changes: 6 additions & 0 deletions exercises/wordy/src/Wordy.elm
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
module Wordy exposing (answer)


answer : String -> Maybe Int
answer problem =
Debug.todo "Please implement this function"
123 changes: 123 additions & 0 deletions exercises/wordy/src/Wordy.example.elm
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
module Wordy exposing (answer)

import Parser as P exposing ((|.), (|=), Parser, Trailing(..))


{-| An `Operation` is a single mathematical operation. It may contain a nested, preceding operation to the left.
-}
type Operation
= Display Int
| Add Number Int
| Multiply Number Int
| Divide Number Int


{-| A `Number` may be a literal integer, or the result of a preceding computation.
-}
type Number
= Number Int
| Computation Operation


{-| Parses a string and solves any problems correctly specified therein or returns `Nothing` in the event of a parse error.
-}
answer : String -> Maybe Int
answer problem =
P.run parser problem
|> Result.toMaybe
|> Maybe.map solveProblem


{-| Solves an `Operation` (possibly including nested operations) for a numerical result.
-}
solveProblem : Operation -> Int
solveProblem operation =
let
toNum num =
case num of
Number i ->
i

Computation p ->
solveProblem p
in
case operation of
Display i ->
i

Add i1 i2 ->
toNum i1 + i2

Multiply i1 i2 ->
toNum i1 * i2

Divide i1 i2 ->
toNum i1 // i2


{-| Parses math problems of the form `"What is <x> [chain of operations]?"`, where `<x>` is an integer and `[chain of operations]` is of the form: `"operation1 n1 operation2 n2 operation3 n3"`, where `n`s are integer and `operation`s are selected from the following list:
- `"plus"` -- Addition
- `"minus"` -- Subtraction
- `"multiplied by"` -- Multiplication
- `"divided by"` -- Division
-}
parser : Parser Operation
parser =
let
packOperations : Int -> List (Number -> Operation) -> Operation
packOperations d l =
case l of
[] ->
Display d

f :: fs ->
List.foldl (\next prev -> next (Computation prev)) (f (Number d)) fs

operation : String -> (Number -> Int -> Operation) -> Parser (Number -> Operation)
operation keyword f =
P.succeed (\i -> \n -> f n i)
|. P.keyword keyword
|= int

plus =
operation "plus" Add

minus =
operation "minus" (\d1 d2 -> Add d1 (negate d2))

multiply =
operation "multiplied by" Multiply

divide =
operation "divided by" Divide
in
P.succeed packOperations
|. P.spaces
|. P.keyword "What is"
|= int
|= P.sequence
{ start = ""
, separator = ""
, end = "?"
, spaces = P.spaces
, item = P.oneOf [ plus, minus, multiply, divide ]
, trailing = Optional
}
|. P.spaces
|. P.end


{-| Parses an integer of the form `"117"` or `"-8"`.
-}
int : Parser Int
int =
P.succeed identity
|. P.spaces
|= P.oneOf
[ P.succeed negate
|. P.symbol "-"
|= P.int
, P.int
]
102 changes: 102 additions & 0 deletions exercises/wordy/tests/Tests.elm
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
module Tests exposing (tests)

import Expect
import Test exposing (..)
import Wordy exposing (answer)


tests : Test
tests =
describe "Wordy"
[ test "just a number" <|
\() ->
Expect.equal (Just 5) <| answer "What is 5?"
, skip <|
test "addition" <|
\() ->
Expect.equal (Just 2) <| answer "What is 1 plus 1?"
, skip <|
test "more addition" <|
\() ->
Expect.equal (Just 55) <| answer "What is 53 plus 2?"
, skip <|
test "addition with negative numbers" <|
\() ->
Expect.equal (Just -11) <| answer "What is -1 plus -10?"
, skip <|
test "large addition" <|
\() ->
Expect.equal (Just 45801) <| answer "What is 123 plus 45678?"
, skip <|
test "subtraction" <|
\() ->
Expect.equal (Just 16) <| answer "What is 4 minus -12?"
, skip <|
test "multiplication" <|
\() ->
Expect.equal (Just -75) <| answer "What is -3 multiplied by 25?"
, skip <|
test "division" <|
\() ->
Expect.equal (Just -11) <| answer "What is 33 divided by -3?"
, skip <|
test "multiple additions" <|
\() ->
Expect.equal (Just 3) <| answer "What is 1 plus 1 plus 1?"
, skip <|
test "addition and subtraction" <|
\() ->
Expect.equal (Just 8) <| answer "What is 1 plus 5 minus -2?"
, skip <|
test "multiple subtraction" <|
\() ->
Expect.equal (Just 3) <| answer "What is 20 minus 4 minus 13?"
, skip <|
test "subtraction then addition" <|
\() ->
Expect.equal (Just 14) <| answer "What is 17 minus 6 plus 3?"
, skip <|
test "multiple multiplication" <|
\() ->
Expect.equal (Just -12) <| answer "What is 2 multiplied by -2 multiplied by 3?"
, skip <|
test "addition and multiplication" <|
\() ->
Expect.equal (Just -8) <| answer "What is -3 plus 7 multiplied by -2?"
, skip <|
test "multiple division" <|
\() ->
Expect.equal (Just 2) <| answer "What is -12 divided by 2 divided by -3?"
, skip <|
test "unknown operation" <|
\() ->
Expect.equal Nothing <| answer "What is 52 cubed?"
, skip <|
test "Non math question" <|
\() ->
Expect.equal Nothing <| answer "Who is the President of the United States?"
, skip <|
test "reject problem missing an operand" <|
\() ->
Expect.equal Nothing <| answer "What is 1 plus?"
, skip <|
test "reject problem with no operands or operators" <|
\() ->
Expect.equal Nothing <| answer "What is?"
, skip <|
test "reject two operations in a row" <|
\() ->
Expect.equal Nothing <| answer "What is 1 plus plus 2?"
, skip <|
test "reject two numbers in a row" <|
\() ->
Expect.equal Nothing <| answer "What is 1 plus 2 1?"
, skip <|
test "reject postfix notation" <|
\() ->
Expect.equal Nothing <| answer "What is 1 2 plus?"
, skip <|
test "reject prefix notation" <|
\() ->
Expect.equal Nothing <| answer "What is plus 1 2?"
]

0 comments on commit fdded4b

Please sign in to comment.