Created
June 18, 2012 15:28
-
-
Save rpdillon/2948914 to your computer and use it in GitHub Desktop.
Evaluate simple infix arthimetic (four operators + parens)
This file contains hidden or 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
# Requires space-delimited input if negative numbers are used | |
def tokenize(s) | |
s.split(/(-?[0-9]+|[*-\/+()])/).map { |t| t.strip }.select { |token| token != "" } | |
end | |
# Evaluates token collections that have only the + and - operators | |
def evaluate(tokens) | |
current = 0 | |
value = Integer(tokens.shift) | |
op = tokens.shift | |
while op | |
num = tokens.shift | |
value = value.send(op, Integer(num)) | |
op = tokens.shift | |
end | |
value | |
end | |
# Evaluates any expressions that have * or / in them | |
def remove_multiply_divide(tokens) | |
while i = tokens.index("*") or i = tokens.index("/") | |
i -= 1 # Move back one token to get the beginning of the expression | |
value = Integer(tokens[i]).send(tokens[i+1], Integer(tokens[i+2])) | |
tokens.slice!(i..i+2) | |
tokens.insert(i, value) | |
end | |
tokens | |
end | |
# Handles parenthetical expressions recursively | |
def eval(tokens) | |
def find_closing_paren(i,tokens) | |
depth = 1 | |
while token = tokens[i] and depth > 0 | |
if token == "(" | |
depth += 1 | |
elsif token == ")" | |
depth -= 1 | |
end | |
i += 1 | |
end | |
i | |
end | |
while i = tokens.index("(") | |
slice = tokens.slice!(i...find_closing_paren(i+1,tokens)) | |
value = eval(slice[1..-2]) | |
tokens.insert(i, value) | |
end | |
evaluate(remove_multiply_divide(tokens)) | |
end | |
# Sort of an ad hoc test framework. | |
tests = [["2" , 2], | |
["-2" , -2], | |
["2 + 4" , 6], | |
["-2 + -6" , -8], | |
["2+3" , 5], | |
["2 - 3 + 4" , 3], | |
["2 + 3 * 4" , 14], | |
["2 * 3 + 4 * 3 / 2" , 12], | |
["(2 + 4) * 3" , 18], | |
["((2) + 4) * 3" , 18], | |
["((2 - 1) + (3 + 1)) * 3" , 15]] | |
passed = 0 | |
tests.each do |test, expected| | |
if eval(tokenize(test)) != expected | |
puts "Failed on input: #{test}" | |
else | |
passed += 1 | |
end | |
end | |
puts "Passed #{passed} tests." |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment