Skip to content

Instantly share code, notes, and snippets.

@stevenvo
Last active July 18, 2016 17:30
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save stevenvo/f1a3a432db484ca905b8e188acbdd8f4 to your computer and use it in GitHub Desktop.
Save stevenvo/f1a3a432db484ca905b8e188acbdd8f4 to your computer and use it in GitHub Desktop.
RPN Calculator - Steven Vo
# 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