如何在Python中评估自定义数学表达式
我正在用Python写一个自定义的掷骰子解析器(你可以笑,但我觉得这很有趣)。简单来说,我想用标准的数学计算方式,但加上一个'd'操作符:
#xdy
sum = 0
for each in range(x):
sum += randInt(1, y)
return sum
比如说,1d6+2d6+2d6-72+4d100 = (5)+(1+1)+(6+2)-72+(5+39+38+59) = 84
我之前用正则表达式把所有的'd'替换成总和,然后再用eval来计算,但当遇到括号时,我的正则表达式就不管用了。有没有比自己实现递归解析更快的方法?或者说能不能给eval加个操作符?
补充一下:我好像给了个不太好的例子,因为上面的例子在我当前的版本中是可以工作的。我想要的是一种方法来计算,比如说(5+(6d6))d(7-2*(1d4))。
我说的“崩溃”只是指我现在的正则表达式失效了。对于我的失败我描述得太模糊了,抱歉让你们困惑了。以下是我现在的代码:
def evalDice(roll_matchgroup):
roll_split = roll_matchgroup.group('roll').split('d')
print roll_split
roll_list = []
for die in range(int(roll_split[0])):
roll = random.randint(1,int(roll_split[1]))
roll_list.append(roll)
def EvalRoll(roll):
if not roll: return 0
rollPattern = re.compile('(?P<roll>\d*d\d+)')
roll_string = rollPattern.sub(evalDice, roll.lower())
在这个情况下,“1d6+4d100”可以正常工作,但“(1d6+4)d100”或者“1d6+4d(100)”就不行了。
5 个回答
6
你可以在使用 re.sub
的时候,搭配一个 回调函数。点击这个链接后,往下找一段以“如果 repl 是一个函数...”开头的文字。
import re
import random
def xdy(matchobj):
x,y=map(int,matchobj.groups())
s = 0
for each in range(x):
s += random.randint(1, y)
return str(s)
s='1d6+2d6+2d6-72+4d100'
t=re.sub('(\d+)d(\d+)',xdy,s)
print(t)
# 5+10+8-72+197
print(eval(t))
# 148
5
Python不允许你自己创建新的运算符,而且你不能用普通的语言来处理括号。你需要写一个递归下降解析器。不过,对于你这个掷骰子的语言来说,这应该是比较简单的。
另外,你也可以利用Python已有的运算符,并使用Python的解析工具把文本转换成抽象语法树(AST)。