评估字符串中的数学表达式
我的问题很简单,假设我有一个字符串,比如 s = 'line $sin(2*x) $x*cos(x) $x'
,而我已经知道了 x
的值(比如是1.0),现在我想把这个字符串计算出来,变成类似 s = line 0.909 -0.416 1.0'
的样子。你可以看到字符串 s 中有三个数学表达式:每个表达式都是以 $ 符号开头,并且以空格或行尾结束。
这个想法应该很简单:使用正则表达式和 re.sub
函数。我之前对正则表达式一无所知,经过一两个小时的学习,我掌握了一些基础知识,但还是搞不清楚怎么写出一个合适的模式来匹配这三个表达式,每个表达式单独匹配。如果我能成功做到这一点,剩下的部分就简单了,使用 eval() 来计算表达式,转换成字符串,然后把整个字符串组合起来返回。下面是我写的代码。
import re
from math import *
# parameters
x = 1.0
# test strings
s = 'line $sin(2*x) $x*cos(x) $x'
p = '\$[\s+]'
def replacer(s):
if s.startswith('$'):
return eval(s[1:])
else:
return "ERROR"
print re.sub(p,replacer,s)
我确定我的正则表达式是错的,因为它没有捕捉到这三个模式,我尝试了很多不同的模式……有没有人能帮我找到一个有效的模式?然后我想我就可以继续做其他部分了。更新:问题解决了,见下面的选定答案。
1 个回答
你第一个遇到的问题是,你在正则表达式中使用了 [\s+]
,这个只会匹配空格或者加号一次。
你想要的正确表达式是 \$(\S+)
,它会把除了$符号以外的所有内容放到一个捕获组里,方便后面使用。
其次,你需要用更符合Python风格的方式来编写代码。把随机的变量和表达式到处乱放是不可重用的。
相反,应该把重复的变量放在一个函数的范围内,这样长期来看会减少麻烦。
在这个情况下,你需要的正则函数不是 re.sub
,而是 re.findall
。这个函数会遍历字符串中的所有匹配项。
你会注意到我在使用正则之前先编译了它,这样可以让代码看起来更整洁。
最后,我们会遍历一个非常简单的 list()
数据类型,里面是我们的正则匹配结果。
如你所见,你可以简单地在任何表达式和任何值上调用 evaluate_expression()
函数。
import re
from math import *
def evaluate_expression(equation, **kwargs):
for key in kwargs:
exec key + " = " + str(kwargs[key]) # Creates x variable
parser = re.compile(r'\$(\S+)')
expressions = parser.findall(equation)
for expression in expressions:
print eval(expression)
evaluate_expression('line $sin(2*x) $x*cos(x) $x', x=1.0)