I want to build a math evaluator for a private use (no securiy limitations when doing eval stuffs) that can give me possible outcomes of mathematical statements. Assume random() here refers to range() and also builtin python range doesn't work with float values. So while it should do normal evals as default, return values should be in either list or set. So, it should eval basic math.
"3" -> [3]
"round(419.9)" -> [420]
"round(3.14, 1)" -> [3.1]
It should also do list evals for all possible outcomes.
"[1,2,3]" -> [1,2,3]
It should also recursively evalulate multiple lists
"[2,1] - 1" -> [1,0]
"[2,1] - [0,1]" -> [2, 1, 0] (with internal evalulation being combination of two list recursive evaulation of expression "[2,1] - 0" and "[2,1] - 1")
"[2,1] * [1,2]" -> [4,2,1]
Then one just need to code random function as range and return list which then evalulated should give answer.
"random(3)" -> [0, 1, 2]
"random(3,4)" -> [3]
"round(random(0.1, 0.5), 1)" -> [0.1, 0.2, 0.3, 0.4]
Finally, it should also have variables and functions from stdlib math modules like
"log(e)" -> [1] # math.log(math.e)
"tau/pi" -> [2.0] # math.tau/math.pi
While this test case is out of scope of question, it would be still cool if something like this can be coded. I have certainly seen some evaluators processing this code fine whereas when I tried using sympy, ast.eval and normal eval, there were significant errors.
"2(3)" -> [6]
"2(3(3))" -> [18]
I managed to come up with code that passes some test cases but its hard to get it all correct.
import random
def evaluate_expression(expression):
# Define safe functions and constants to use within eval
def custom_function(expression, roundval=0):
if isinstance(expression, list):
return [round(item, roundval) for item in expression]
else:
return round(expression, roundval)
safe_dict = {
'random': lambda *args: list(range(int(args[0]), int(args[1]) + 1)),
'round': custom_function,
}
# Add some common mathematical constants
safe_dict.update({
'pi': 3.141592653589793,
'e': 2.718281828459045
})
# Try to evaluate the expression
try:
result = eval(expression, {"__builtins__": None}, safe_dict)
# If the result is a single number, return it in a list
if isinstance(result, (int, float)):
return [result]
# If the result is a list, return it as is
elif isinstance(result, list):
return result
else:
raise ValueError("Unsupported result type")
except (SyntaxError, NameError, TypeError, ValueError) as e:
return str(e)
These are the test cases
# Test cases
assert evaluate_expression("3") == [3]
assert evaluate_expression("round(419.9)") == [420]
assert evaluate_expression("round(3.14, 1)") == [3.1]
assert evaluate_expression("[1,2,3]") == [1,2,3]
assert evaluate_expression("[2,1] - 1") == [1,0]
assert evaluate_expression("[2,1] - [0,1]") == [2, 1, 0]
assert evaluate_expression("[2,1] * [1,2]") == [4,2,1]
assert evaluate_expression("random(3)") == [0,1,2]
assert evaluate_expression("random(3, 4)") == [3]
assert evaluate_expression("round(random(0.1, 0.5), 1)") == [0.1, 0.2, 0.3, 0.4]
assert evaluate_expression("log(e)") == [1]
assert evaluate_expression("tau/pi") == [2.0]
#out of scope
assert evaluate_expression("2(3)") == [6]
assert evaluate_expression("2(3(3))") == [18]
I think if this test case passes, something like "random(1,9) * random(1,9)" shouldn't error out and should produce "[1,2,3,4,5,6,7,8]*[1,2,3,4,5,6,7,8]" which then evaulated should generate a big list. As, a sidenote, I also managed to generate a custom random range generator. (I dont care much about 0.5 being in list when input is (0.01, 0.05) but it would be cool if this function can be improved)
def custom_random(start, end):
ten = 10**len(str(start).split('.')[-1])
if isinstance(start, int):
mul = 1
elif isinstance(start, float):
if len(str(start)) == 3:
mul = 0.1
elif len(str(start)) == 4:
mul = 0.01
if isinstance(start, int) and isinstance(end, int):
return list(range(start, end))
elif isinstance(start, float) and isinstance(end, float):
return [round(i * mul, len(str(start).split('.')[1])) for i in range(int(start * ten), int(end * ten) + 1)]
else:
raise TypeError("Unsupported input type")
print(custom_random(1, 5))
print(custom_random(3, 4))
print(custom_random(10, 50))
print(custom_random(0.1, 0.5)) #prints also 0.5 but it should not print 0.5, but only upto 0.4? but not big of bug anyways
print(custom_random(0.01, 0.05)) #prints also 0.05 but it should not print 0.05, but only upto 0.4? but not big of bug anyways
print(custom_random(0.05, 0.09))
from I can use random to get a random value from a range. How do I make math evaluator that does opposite and also has access to math modules?
No comments:
Post a Comment