Last active
July 18, 2016 17:30
-
-
Save stevenvo/f1a3a432db484ca905b8e188acbdd8f4 to your computer and use it in GitHub Desktop.
RPN Calculator - Steven Vo
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
# RPN Calculator | |
# | |
# SPECIFICATIONS: | |
# REQ 1: The calculator should use standard input and standard output, unless the language makes that impossible. | |
# REQ 2: It should implement the four standard arithmetic operators | |
# REQ 3: It should support negative and decimal numbers, and should not have arbitrary limits on the number of operations. | |
# REQ 4: The calculator should not allow invalid or undefined behavior. | |
# REQ 5: The calculator should exit when it receives a q command or an end of input indicator (EOF). | |
class RpnStack | |
def initialize | |
@stack = Array.new | |
end | |
def pop | |
@stack.pop | |
end | |
def push(element) | |
is_number = true if Float(element) rescue false | |
if is_number | |
@stack.push(element) | |
return element | |
else | |
return self.calculate(element) | |
end | |
end | |
def calculate(operator) | |
is_operator = true if ['+', '-', '*', '/'].include?(operator) else false | |
# REQ #2: supports 4 standard arithmetic operators | |
if !is_operator | |
return 'Invalid Input' | |
end | |
rescue_operands = Array.new # to save popped item for future disaster recovery | |
begin | |
# Retrieve and convert operand 1 | |
val = @stack.pop | |
rescue_operands.push(val) if !val.nil? # save popped item for future disaster recovery | |
operand_1 = Float(val) | |
# Retrieve and convert operand 2 | |
val = @stack.pop | |
rescue_operands.push(val) if !val.nil? # save popped item for future disaster recovery | |
operand_2 = Float(val) | |
result = 0 | |
case operator | |
when '+' | |
result = operand_2 + operand_1 | |
when '-' | |
result = operand_2 - operand_1 | |
when '*' | |
result = operand_2 * operand_1 | |
when '/' | |
result = operand_2 / operand_1 | |
if result == Float::INFINITY || result.nan? then raise ZeroDivisionError end | |
# Use ZeroDivisionError to handle both NaN, Infinity, or ZeroDivisionError as below | |
# ruby-1.9.2-p180 :018 > 0.0 / 0 | |
# => NaN | |
# | |
# ruby-1.9.2-p180 :020 > 3.0 / 0 | |
# => Infinity | |
# | |
# ruby-1.9.2-p180 :021 > 3 / 0 | |
# ZeroDivisionError: divided by 0 | |
else | |
puts 'Not a valid operator!' # REQ #4: disallow invalid or undefined behavior | |
end | |
@stack.push(result) | |
return result | |
rescue TypeError => e | |
# Put the "already popped" items back to the stack | |
while !(item = rescue_operands.pop).nil? | |
@stack.push(item) | |
end | |
return 'Invalid Input' # REQ #4: disallow invalid or undefined behavior | |
rescue ZeroDivisionError | |
# Put the "already popped" items back to the stack | |
while !(item = rescue_operands.pop).nil? | |
@stack.push(item) | |
end | |
return 'Division by ZERO is detected, please try with different operator!' # REQ #4: disallow invalid or undefined behavior | |
end | |
return 'Invalid Input!' # REQ #4: disallow invalid or undefined behavior | |
end | |
def size | |
@stack.size | |
end | |
def to_s | |
@stack.to_s | |
end | |
end | |
rpn_stack = RpnStack.new | |
while true | |
print "#{rpn_stack.to_s} > " # print the stack content before the prompt for better visibility | |
input = gets.chomp.to_s.strip() | |
break if 'q'.eql?(input) or input.empty? # REQ #4: exit when enter 'q' or EOF | |
puts rpn_stack.push(input) | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment