 paulfioravanti's solution

to Wordy in the Ruby Track

Published at Jul 13 2018 · 0 comments
Instructions
Test suite
Solution

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

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)

Bonus — Exponentials

If you'd like, handle exponentials.

What is 2 raised to the 5th power?

32

For installation and learning resources, refer to the Ruby resources page.

For running the tests provided, you will need the Minitest gem. Open a terminal window and run the following command to install minitest:

gem install minitest

If you would like color output, you can require 'minitest/pride' in the test file, or note the alternative instruction, below, for running the test file.

Run the tests from the exercise directory using the following command:

ruby wordy_test.rb

To include color from the command line:

ruby -r minitest/pride wordy_test.rb

Source

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

Submitting Incomplete Solutions

It's possible to submit an incomplete solution so you can see how others have completed the exercise.

wordy_test.rb

require 'minitest/autorun'
require_relative 'wordy'

# Common test data version: 1.2.0 86d0069
class WordyTest < Minitest::Test
# skip
problem = WordProblem.new("What is 1 plus 1?")
end

skip
problem = WordProblem.new("What is 53 plus 2?")
end

skip
problem = WordProblem.new("What is -1 plus -10?")
end

skip
problem = WordProblem.new("What is 123 plus 45678?")
end

def test_subtraction
skip
problem = WordProblem.new("What is 4 minus -12?")
end

def test_multiplication
skip
problem = WordProblem.new("What is -3 multiplied by 25?")
end

def test_division
skip
problem = WordProblem.new("What is 33 divided by -3?")
end

skip
problem = WordProblem.new("What is 1 plus 1 plus 1?")
end

skip
problem = WordProblem.new("What is 1 plus 5 minus -2?")
end

def test_multiple_subtraction
skip
problem = WordProblem.new("What is 20 minus 4 minus 13?")
end

skip
problem = WordProblem.new("What is 17 minus 6 plus 3?")
end

def test_multiple_multiplication
skip
problem = WordProblem.new("What is 2 multiplied by -2 multiplied by 3?")
end

skip
problem = WordProblem.new("What is -3 plus 7 multiplied by -2?")
message = "You should ignore order of precedence. -3 + 7 * -2 = -8, not #{problem.answer}"
end

def test_multiple_division
skip
problem = WordProblem.new("What is -12 divided by 2 divided by -3?")
end

def test_unknown_operation
skip
problem = WordProblem.new("What is 52 cubed?")
assert_raises(ArgumentError) do
end
end

def test_non_math_question
skip
problem = WordProblem.new("Who is the President of the United States?")
assert_raises(ArgumentError) do
end
end
end
# frozen_string_literal: true

class WordProblem
MATH_QUESTION =
/\A
What\sis\s                           # What is
(?<initial_value>-?\d+)\s            # 1, -1 etc
(?<messages>(\w+(\sby)?\s-?\d+\s?)+) # plus 1, multiplied by 1 plus 2 etc
\?                                   # question mark at the end
\z/x.freeze
private_constant :MATH_QUESTION
MESSAGES = {
"plus" => :+,
"minus" => :-,
"multiplied" => :*,
"divided" => :/
}.freeze
private_constant :MESSAGES

def initialize(question)
@question = question
end

raise ArgumentError unless (match = question.match(MATH_QUESTION))

initial_value = match[:initial_value].to_i
messages = parse_messages(match[:messages])
messages.reduce(initial_value) do |acc, (message, value)|
acc.public_send(message, value)
end
end

private

def parse_messages(messages)
messages
.gsub("by ", "")
.split
.each_slice(2)
.map { |message, value| [MESSAGES[message], value.to_i] }
end
end