Skip to content

Instantly share code, notes, and snippets.

@av-ast
Created July 18, 2012 22:05
Show Gist options
  • Save av-ast/3139224 to your computer and use it in GitHub Desktop.
Save av-ast/3139224 to your computer and use it in GitHub Desktop.
Calculator and converter for Reverse Polish Notation
module PolishCalc
OPERATORS = {
"+" => {:priority => 0, :arity => 2},
"-" => {:priority => 0, :arity => 2},
"*" => {:priority => 1, :arity => 2},
"/" => {:priority => 1, :arity => 2}
}
class Stack
attr_reader :stack
def initialize
@stack = []
[:shift, :first, :size, :empty?, :any?].each do |method|
define_singleton_method method do
@stack.send method
end
end
end
def unshift(elem)
@stack.unshift elem
end
def slice!(range)
@stack.slice! range
end
end
class Base
def initialize
@stack = PolishCalc::Stack.new
@operators = PolishCalc::OPERATORS
end
def operand?(str)
!!(str =~ /^\d+$/)
end
end
class Calculator < PolishCalc::Base
def run(polish_arr=[])
polish_arr.each do |token|
if operand?(token)
@stack.unshift token.to_f
else
raise "WrongOperator" unless @operators[token]
arity = @operators[token][:arity]
raise "WrongArity" if arity > @stack.size
args = @stack.slice!(0..arity-1)
case arity
when 2
@stack.unshift args[1].send(token, args[0])
else
raise "ArityNotImplemented"
end
end
end
@stack.shift.to_f
end
end
class Converter < PolishCalc::Base
def left_brace?(str)
str == "("
end
def right_brace?(str)
str == ")"
end
def priority(operator)
raise "WrongOperator" unless @operators[operator]
@operators[operator][:priority]
end
def tokens_positions(str, rgxp)
str.enum_for(:scan, rgxp).map { Regexp.last_match.begin(0) }
end
def prepare_infix_arr(infix_str)
infix_str = infix_str.gsub(/\s/, "")
digits_pos = tokens_positions(infix_str, /\d+/)
operators_pos = tokens_positions(infix_str, /[\+\*\-\/\(\)]/)
(digits_pos + operators_pos).sort.map {|pos| infix_str[pos..-1][/[\+\*\-\/\(\)]|\d+/] }
end
def infix2polish(infix = '')
polish = []
infix_arr = prepare_infix_arr(infix)
infix_arr.each do |token|
if operand?(token)
polish << token
elsif left_brace?(token)
@stack.unshift token
elsif right_brace?(token)
if @stack.stack.index("(").nil?
raise "LeftBraceNotFound"
end
while @stack.any? do
cur_stack_elem = @stack.shift
polish << cur_stack_elem unless cur_stack_elem == "("
end
else
if @stack.empty?
@stack.unshift token
else
j = 0
while @stack.size < j do
polish << @stack.shift if priority(token) <= priority(@stack.first)
j+=1
end
@stack.unshift token
end
end
end
while @stack.any? do
polish << @stack.shift
end
polish
end
end
end
converter = PolishCalc::Converter.new
calc = PolishCalc::Calculator.new
puts "Enter infix expr: "
infix = gets
polish_arr = converter.infix2polish(infix)
puts "Reverse Polish Notation: "
puts polish_arr.join(" ")
puts "Result: "
puts calc.run(polish_arr)
=begin
1:57 ava@avacomp:~>ruby polish_calc.rb
Enter infix expr:
1 + 1
Reverse Polish Notation:
1 1 +
Result:
2.0
1:58 ava@avacomp:~>ruby polish_calc.rb
Enter infix expr:
3 + 4 * (2 - 1)
Reverse Polish Notation:
3 4 2 1 - * +
Result:
7.0
1:59 ava@avacomp:~>ruby polish_calc.rb
Enter infix expr:
3 + 4 * (2 - 1) - 10 / 5
Reverse Polish Notation:
3 4 2 1 - * + 10 5 / -
Result:
5.0
1:59 ava@avacomp:~>ruby polish_calc.rb
Enter infix expr:
0
Reverse Polish Notation:
0
Result:
0.0
2:00 ava@avacomp:~>ruby polish_calc.rb
Enter infix expr:
2 / 0
Reverse Polish Notation:
2 0 /
Result:
Infinity
=end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment