如何在Python中评估自定义数学表达式

时间:2010-04-15 04:03:48

标签: python math eval

我正在python中编写一个自定义骰子滚动解析器(如果必须,可以使用snicker)。基本上,我想使用标准数学评估,但添加'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

我正在使用正则表达式来替换所有'和s,然后使用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 个答案:

答案 0 :(得分:6)

您可以将callback functionre.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

答案 1 :(得分:5)

Python不允许你编写全新的操作符,也不能用常规语言编写括号。你必须编写一个递归下降解析器。对于你的骰子滚动语言,这应该非常简单。

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

答案 2 :(得分:2)

查看PyParsing库。特别是,examples页面的sample非常接近您想要的内容:

  

dice2.py

     

骰子滚动解析器和评估器,用于评估字符串,例如“4d20 + 5.5 + 4d6.takeHighest(3)”。

答案 3 :(得分:0)

这使用了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) for x in 4 * [6]))

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

((random.randint(1,x) for x in [6,6,6,6]))是生成器等同于列表理解;这个特殊的将返回1到6之间的四个随机数。

答案 4 :(得分:0)

在我的Supybot dice plugin中,我用

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

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