如何在Python中计算自定义数学表达式

如何在Python中计算自定义数学表达式,python,math,eval,Python,Math,Eval,我正在用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替换为sum,然后用eval,但是我的正则表达式在处理两边的括号时崩溃了。有没有比实现

我正在用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替换为sum,然后用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())
def evalDice(滚动匹配组):
滚动分割=滚动匹配组。组('roll')。分割('d'))
打印卷分割
滚动列表=[]
对于范围内的模具(int(滚动分割[0]):
roll=random.randint(1,int(roll\u split[1]))
滚动列表。追加(滚动)
def蒸发辊(辊):
如果没有滚动:返回0
rollPattern=re.compile(“(?P\d*d\d+”)
roll\u string=rollPattern.sub(evalDice,roll.lower())

为此,“1d6+4d100”可以正常工作,但是“(1d6+4)d100”甚至“1d6+4d(100)”都失败了。

Python不允许您编写全新的运算符,也不能用常规语言编写括号。您必须编写一个递归下降解析器。不过,这对于掷骰子语言来说应该很简单

或者,您可以使用现有的Python操作符,并使用Python解析工具将文本转换为AST。

您可以使用with
re.sub
。当您跟随链接时,向下搜索以“If repl is a function…”开头的段落


这用的是eval,这真的很糟糕,但给你

>>> x = '1d6+2d6+2d6-72+4d100'
>>> eval(re.sub(r'(\d+)d(\d+)',r'sum((random.randint(1,x) for x in \1 * [\2]))', x))

一些简要说明:

比如说,它将
4d6
替换为
sum((random.randint(1,x)表示4*[6]中的x))

4*[6]
生成列表
[6,6,6]

([6,6,6,6]中x的random.randint(1,x))
是列表理解的生成器等价物;这个特殊的函数将返回四个介于1和6之间的随机数。

请查看库。特别是,该页面与您想要的内容非常接近:

骰子滚动分析器和计算器,用于计算字符串,如“4d20+5.5+4d6.3”

在我的示例中,我使用

r'(?P<sign>[+-])((?P<dice>\d*)d(?P<sides>\d+)|(?P<mod>\d+))'
r'(?P[+-])(?P\d*)d(?P\d+)|(?P\d+))

然后获得每个骰子的总数和一个总修改器,滚动它们并获得总结果(我想显示每个骰子的总数)。

你能告诉我们你尝试了什么以及失败的原因吗?结果只是生成了一个递归辅助函数。谢谢你的帮助!我认为parens只是一个示范性的中间结果,而不是DSL本身的一部分。写
1d6+2d6+2d6-72+4d100=84
就没有那么多解释性了+不过,对于rec下降建议,我还是很困惑。为什么OP的“处理括号时正则表达式[崩溃]”?我比我想象的要模糊,对不起。上面的例子很好用。我的意思是,如果你把括号放在d的两边(比如(4+2)d6),解释器就无法处理这个问题。
r'(?P<sign>[+-])((?P<dice>\d*)d(?P<sides>\d+)|(?P<mod>\d+))'