Created
February 23, 2018 01:38
-
-
Save bbuchalter/439cf721d3c5f558b91413261f7e445f to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# Create a calculator that takes integers separated by the basic mathematical operators (+,-,*,/) and returns the result. | |
# You can use Ruby's mathamatical methods(+, -, *, /). | |
# The priority is on working code, not brevity. | |
# 7 + 2 - 3 / 4 * 5 = 5.25 | |
#math_characters = '7 + 2 - 3 / 4 * 5' | |
# magic | |
#math_result = 5.25 | |
#puts "Result of #{math_characters} is: #{math_result}" | |
# Sandi's four steps to refactoring a class extraction | |
# 1. parse the new code | |
# 2. parse and execute it | |
# 3. parse, execute, use result | |
# 4. delete unused code | |
# permutations of order of operations, excluding exponents and roots | |
# 1. multiply, divide | |
# 2. multiply, add | |
# 3. multiply, subtract | |
# 4. multiply, multiply | |
# 5. divide, multiple | |
# 6. divide, add | |
# 7. divide, subtract | |
# 8. divide, divide | |
# 9. add, multiply | |
# 10. add, divide | |
# 11. add, subtract | |
# 12. add, add | |
# 13. subtract, multiply | |
# 14. subtract, divide | |
# 15. subtract, add | |
# 16. subtract, subtract | |
require 'rspec/autorun' | |
module Equation | |
# @params equation[String] | |
def initialize(equation) | |
@equation = equation | |
end | |
private | |
attr_reader :equation | |
def equation_as_array | |
@equation_as_array ||= equation.split(" ") | |
end | |
end | |
class StringMath | |
include Equation | |
# @return Float | |
def result | |
first_number = equation_as_array[0].to_f | |
operation = equation_as_array[1].to_sym | |
second_number = equation_as_array[2].to_f | |
first_number.send(operation, second_number) | |
end | |
end | |
RSpec.describe StringMath do | |
subject { described_class.new(equation).result } | |
context "multiply numbers from a string" do | |
let(:equation) { "4 * 3" } | |
it { is_expected.to eq(12) } | |
end | |
context "divide numbers from a string" do | |
let(:equation) { "6 / 3" } | |
it { is_expected.to eq(2) } | |
end | |
context "addition numbers from a string" do | |
let(:equation) { "6 + 3" } | |
it { is_expected.to eq(9) } | |
end | |
context "subtract numbers from a string" do | |
let(:equation) { "6 - 3" } | |
it { is_expected.to eq(3) } | |
end | |
context "divides into non whole numbers" do | |
let(:equation) { "6 / 4" } | |
it { is_expected.to eq(6 / 4.0) } | |
end | |
end | |
class StringCalculator | |
include Equation | |
# Recurse through each sub-equation until there is only a single value left. | |
# @return Float | |
def result | |
if result_found? | |
equation.to_f | |
else | |
StringCalculator.new(simplified_equation).result | |
end | |
end | |
private | |
# @return Boolean | |
def result_found? | |
equation_as_array.length == 1 | |
end | |
# Replace the first sub equation with the results of that equation | |
# @return String | |
def simplified_equation | |
equation.sub(sub_equation, sub_result_as_string) | |
end | |
# Extract the first sub equation from the current equation | |
# @return String | |
def sub_equation | |
@sub_equation ||= SubEquation.new(equation).extract | |
end | |
# Calculate the result of the sub_equation | |
# @return String | |
def sub_result_as_string | |
@sub_result ||= StringMath.new(sub_equation).result.to_s | |
end | |
end | |
RSpec.describe StringCalculator do | |
subject { described_class.new(equation).result } | |
context "given division followed by multiplication" do | |
let(:equation) { "6 / 3 * 5" } | |
it "follows the order of operations" do | |
expect(subject).to eq(10) | |
end | |
end | |
context "given multiplication followed by division" do | |
let(:equation) { "6 * 3 / 3" } | |
it "follows the order of operations" do | |
expect(subject).to eq(6) | |
end | |
end | |
context "given multiplication followed by division followed by addition" do | |
let(:equation) { "6 * 3 / 3 + 1" } | |
it "follows the order of operations" do | |
expect(subject).to eq(7) | |
end | |
end | |
context "given addition followed division" do | |
let(:equation) { "6 + 3 / 9" } | |
it "follows the order of operations" do | |
expect(subject).to eq(6 + 3 / 9.0) | |
end | |
end | |
context "given addition followed division" do | |
let(:equation) { "6 + 3 / 10" } | |
it "follows the order of operations" do | |
expect(subject).to eq(6.3) | |
end | |
end | |
context "many operations" do | |
let(:equation) { "7 + 2 - 3 / 4 * 5" } | |
it "follows the order of operations", focus: true do | |
expect(subject).to eq(5.25) | |
end | |
end | |
end | |
class NextOperation | |
include Equation | |
# @return Integer | |
def index | |
first_multiplication_or_division || first_addition_or_subtraction | |
end | |
private | |
def first_multiplication_or_division | |
[equation.index("*"), equation.index("/")].compact.min | |
end | |
def first_addition_or_subtraction | |
[equation.index("+"), equation.index("-")].compact.min | |
end | |
end | |
RSpec.describe NextOperation do | |
describe "#index" do | |
subject { described_class.new(equation).index } | |
context "given division followed by multiplication" do | |
let(:equation) { "6 / 3 * 5" } | |
it { is_expected.to eq 2 } | |
end | |
context "given division followed by addition" do | |
let(:equation) { "6 / 3 + 5" } | |
it { is_expected.to eq 2 } | |
end | |
context "given division followed by subtraction" do | |
let(:equation) { "6 / 3 - 5" } | |
it { is_expected.to eq 2 } | |
end | |
context "given division followed by division" do | |
let(:equation) { "6 / 3 / 5" } | |
it { is_expected.to eq 2 } | |
end | |
context "given addition followed by division" do | |
let(:equation) { "6 + 3 / 5" } | |
it { is_expected.to eq 6 } | |
end | |
context "given addition followed by subtraction" do | |
let(:equation) { "6 + 3 - 5" } | |
it { is_expected.to eq 2 } | |
end | |
context "given addition followed by addition" do | |
let(:equation) { "6 + 3 + 5" } | |
it { is_expected.to eq 2 } | |
end | |
context "given subtraction followed by multiplication" do | |
let(:equation) { "6 + 3 * 5" } | |
it { is_expected.to eq 6 } | |
end | |
context "given subtraction followed by division" do | |
let(:equation) { "6 - 3 / 5" } | |
it { is_expected.to eq 6 } | |
end | |
context "given subtraction followed by multiplication" do | |
let(:equation) { "6 - 3 * 5" } | |
it { is_expected.to eq 6 } | |
end | |
context "given subtraction followed by addition" do | |
let(:equation) { "6 - 3 + 5" } | |
it { is_expected.to eq 2 } | |
end | |
context "given subtraction followed by subtraction" do | |
let(:equation) { "6 - 3 - 5" } | |
it { is_expected.to eq 2 } | |
end | |
context "given addition followed by division" do | |
let(:equation) { "6 + 3 / 5" } | |
it { is_expected.to eq 6 } | |
end | |
context "given addition followed by multiplication" do | |
let(:equation) { "6 + 3 * 5" } | |
it { is_expected.to eq 6 } | |
end | |
context "given addition followed by addition" do | |
let(:equation) { "6 + 3 + 5" } | |
it { is_expected.to eq 2 } | |
end | |
context "given addition followed by subtraction" do | |
let(:equation) { "6 + 3 - 5" } | |
it { is_expected.to eq 2 } | |
end | |
context "given no operation" do | |
let(:equation) { "6" } | |
it { is_expected.to eq nil } | |
end | |
end | |
end | |
class SubEquation | |
include Equation | |
# @return String | |
def extract | |
if next_operation | |
sub_equation_as_string | |
else | |
equation | |
end | |
end | |
private | |
def sub_equation_as_string | |
"#{equation_as_array[next_operation-1]} #{equation_as_array[next_operation]} #{equation_as_array[next_operation+1]}" | |
end | |
def next_operation | |
@next_operation ||= NextOperation.new(equation_as_array).index | |
end | |
end | |
RSpec.describe SubEquation do | |
describe "#extract" do | |
subject { described_class.new(equation).extract } | |
context "given division followed by multiplication" do | |
let(:equation) { "6 / 3 * 5" } | |
it { is_expected.to eq "6 / 3" } | |
end | |
context "given division followed by addition" do | |
let(:equation) { "6 / 3 + 5" } | |
it { is_expected.to eq "6 / 3" } | |
end | |
context "given division followed by subtraction" do | |
let(:equation) { "6 / 3 - 5" } | |
it { is_expected.to eq "6 / 3" } | |
end | |
context "given division followed by division" do | |
let(:equation) { "6 / 3 / 5" } | |
it { is_expected.to eq "6 / 3" } | |
end | |
context "given addition followed by division" do | |
let(:equation) { "6 + 3 / 5" } | |
it { is_expected.to eq "3 / 5" } | |
end | |
context "given addition followed by subtraction" do | |
let(:equation) { "6 + 3 - 5" } | |
it { is_expected.to eq "6 + 3" } | |
end | |
context "given addition followed by addition" do | |
let(:equation) { "6 + 3 + 5" } | |
it { is_expected.to eq "6 + 3" } | |
end | |
context "given subtraction followed by multiplication" do | |
let(:equation) { "6 + 3 * 5" } | |
it { is_expected.to eq "3 * 5" } | |
end | |
context "given subtraction followed by division" do | |
let(:equation) { "6 - 3 / 5" } | |
it { is_expected.to eq "3 / 5" } | |
end | |
context "given subtraction followed by multiplication" do | |
let(:equation) { "6 - 3 * 5" } | |
it { is_expected.to eq "3 * 5" } | |
end | |
context "given subtraction followed by addition" do | |
let(:equation) { "6 - 3 + 5" } | |
it { is_expected.to eq "6 - 3" } | |
end | |
context "given subtraction followed by subtraction" do | |
let(:equation) { "6 - 3 - 5" } | |
it { is_expected.to eq "6 - 3" } | |
end | |
context "given addition followed by division" do | |
let(:equation) { "6 + 3 / 5" } | |
it { is_expected.to eq "3 / 5" } | |
end | |
context "given addition followed by multiplication" do | |
let(:equation) { "6 + 3 * 5" } | |
it { is_expected.to eq "3 * 5" } | |
end | |
context "given addition followed by addition" do | |
let(:equation) { "6 + 3 + 5" } | |
it { is_expected.to eq "6 + 3" } | |
end | |
context "given addition followed by subtraction" do | |
let(:equation) { "6 + 3 - 5" } | |
it { is_expected.to eq "6 + 3" } | |
end | |
context "given no operation" do | |
let(:equation) { "6" } | |
it { is_expected.to eq "6" } | |
end | |
context "given operations with non-whole numbers" do | |
let(:equation) { "2.0 * 0.33333333 / 1.2354" } | |
it { is_expected.to eq "2.0 * 0.33333333" } | |
end | |
context "given no operation with non-whole numbers" do | |
let(:equation) { "0.33333333" } | |
it { is_expected.to eq "0.33333333" } | |
end | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment