π Exercism Research is now launched. Help Exercism, help science and have some fun at research.exercism.io π

# jdsutherland's solution

## to Wordy in the Ruby Track

Published at Dec 04 2019 · 0 comments
Instructions
Test suite
Solution

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

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``````

### wordy.rb

``````class WordProblem
SUB  = 'minus'.freeze
MULT = 'multiplied'.freeze
DIV  = 'divided'.freeze
POW  = 'raised'.freeze
OPS = {
SUB  => :-,
MULT => :*,
DIV  => :/,
POW  => :**,
}.freeze
REG_WHITELIST = /What is|to the|by|power|[- ?]|\d+(st|nd|rd|th)?/.freeze
REG_OPS = Regexp.new(OPS.keys.join('|')).freeze
REG_FULL_WHITELIST = Regexp.new([REG_WHITELIST.source, REG_OPS.source].join('|')).freeze

def initialize(question)
validate!(question)

@nums = question.scan(/-?\d+/).map(&:to_f)
@ops = question.scan(REG_OPS)
end

ops.each_with_index
.reduce(nums.first) { |acc, (op, idx)| calc(op, acc, nums[idx+1]) }
end

private

def validate!(question)
raise ArgumentError if question.gsub(REG_FULL_WHITELIST, '').length > 0
end

def calc(op, a, b)
a.send(OPS.fetch(op), b)
end
end``````

### wordy_test.rb

``````require 'minitest/autorun'
require_relative 'wordy'

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

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

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

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

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

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

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

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

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

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

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

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

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
problem = WordProblem.new("What is -12 divided by 2 divided by -3?")
end

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

def test_non_math_question
assert_raises(ArgumentError) do
WordProblem.new("Who is the President of the United States?")
end
end
end``````