I am trying to solve a problem namely:
Instructions
Given a mathematical expression as a string you must return the result as a number.
Numbers
Number may be both whole numbers and/or decimal numbers. The same goes for the returned result.
Operators
You need to support the following mathematical operators:
Multiplication *
Division /
Addition +
Subtraction -
Operators are always evaluated from left-to-right, and * and / must be evaluated before + and -.
Parentheses
You need to support multiple levels of nested parentheses, ex. (2 / (2 + 3.33) * 4) - -6
Whitespace
There may or may not be whitespace between numbers and operators.
An addition to this rule is that the minus sign (-) used for negating numbers and parentheses will never be separated by whitespace. I.e., all of the following are valid expressions.
1-1 // 0
1 -1 // 0
1- 1 // 0
1 - 1 // 0
1- -1 // 2
1 - -1 // 2
6 + -(4) // 2
6 + -( -4) // 10
And the following are invalid expressions
1 - - 1 // Invalid
1- - 1 // Invalid
6 + - (4) // Invalid
6 + -(- 4) // Invalid
Validation
Thus making '2 /2+3 * 4.75- -6'
a valid expression. I have been able to code the polish form for expressions which don't respect the whitespaces but don't give negative numbers. I think I can solve the problem for negative numbers if they respect the whitespaces. My problem is how to actually tokenize the input expression if the whitespaces are not respected and negative numbers are given. This is my algorithm so far:
def is_operator? s
operators = ['*', '/', '+', '-']
operators.include?(s)
end
def is_operand? s
!(s =~ /^[0-9.]+$/).nil?
end
def priority op
case op
when "(" , ")" then 0
when "/", "*" then 2
else 1
end
end
def eval(lt,rt,op)
case op
when '+' then lt.to_f + rt.to_f
when '-' then lt.to_f - rt.to_f
when '*' then lt.to_f * rt.to_f
when '/' then lt.to_f / rt.to_f
end
end
def indent_string s
s.gsub(/[^[0-9.]]/) { |m| " #{m} "}.split(" ")
end
def create_polish s
stack = Array.new()
array = indent_string s
fpp = ""
array.each do |item|
if is_operand? item
fpp = fpp + item + " "
else
if item == '('
stack << item
else if is_operator? item
while stack.any? && ( priority(stack[-1]) >= priority(item) )
fpp = fpp + stack.pop + " "
end
stack << item
else
while stack.any? && !(stack[-1] == '(' )
fpp = fpp + stack.pop + " "
end
stack.pop
end
end
end
end
while stack.any?
fpp = fpp + stack.pop + " "
end
fpp
end
def solve_polish s
stack = Array.new()
s.split(" ").each do |item|
unless is_operator? item
stack << item
else
elements = stack.pop(2)
stack << eval(elements[0], elements[1], item)
end
end
puts stack.pop
end
solve_polish(create_polish '(5 + 2) * 9 - 10 + ( 7 * (2 + 3) ) - 3 * (2)')
it solves non-negative expressions that don't respect the whitespace rule because I made indent_string method which puts a space before and after each operator and then I just split the string to get the tokens. This way I lose negative numbers sadly. Any ideas?
UPDATE 1: Having thought about it I would need a regex that puts whitespaces front and back if there is no other operator behind him. So '2- -2' would get transformed in '2 - -2' because the second '-' has a '-' preceeding him instead of another number.