数学表达式求值

9
什么是实现一个Python程序的最佳方法,该程序将接收一个字符串并根据运算符优先级输出其结果(例如:“4 + 3 * 5”将输出19)。我已经搜索了解决此问题的方法,但它们都太复杂了,我正在寻找一个(相对)简单的方法。
澄清:我需要比eval()略微更高级的东西-我希望能够添加其他运算符(例如最大运算符-4 $ 2 = 4),而且我更感兴趣的是学术上的问题,而不是职业上的问题-我想知道如何做到这一点。

1
http://docs.python.org/reference/simple_stmts.html#exec - nlucaroni
1
请查看:https://dev59.com/4kbRa4cB1Zd3GeqPxBrM - Bart Kiers
1
尝试使用“评估”而不是“解决”,因为后者暗示可以在给定“4x-6 = 4”的情况下找到“x”。 - dmckee --- ex-moderator kitten
3
"我想配置其他的运营商。" 叹息声,你一开始就不能这么说吗? - Lennart Regebro
3
编译器资源:https://dev59.com/x3VD5IYBdhLWcg3wXaed 和一些非常简短的实现:https://dev59.com/CXM_5IYBdhLWcg3waiiJ 和 http://stackoverflow.com/questions/1538964/code-golf-reverse-polish-notation-postfix-evaluator 和 https://dev59.com/j3NA5IYBdhLWcg3whuR_。 - dmckee --- ex-moderator kitten
5个回答

16

如果您是“学术兴趣者”,您想学习如何使用运算符优先级编写解析器。

Python中的简单自顶向下解析 是一篇不错的文章,它构建了一个示例解析器,可以完成您想要做的事情:计算数学表达式。

我强烈推荐尝试编写您自己的第一个解析器--这是那些“啊,原来是这样”的时刻之一!


我快速浏览了一下,似乎你提供的链接中的文章是在Python中实现解释器模式。 - Thomas Owens
该链接返回一个404错误。 - Derek

2
另一种可能性是使用通用解析器构建器Pyparsing。它比你需要的更强大,但实现可能更快。

pyparsing的维基(pyparsing.wikispaces.com)包含了一些算术表达式解析器的例子 - fourFn.py和simpleArith.py。即使您不使用pyparsing,fourFn.py也很有可能让您对解析器如何实现运算符优先级有所启发。 - PaulMcG
我刚意识到原帖想要添加其他运算符。simpleArith.py展示了如何添加一个阶乘(!)运算符 - evalArith.py(页面底部)扩展了simpleArith.py并展示了如何评估解析出的值。 - PaulMcG

1

这就是 Python 中 "eval" 函数的作用。

result = eval(expression)

需要注意的是,它可以做更多的事情,主要是调用函数,因此为了安全起见,您应该确保它无法访问本地或全局变量。此外,您可以访问内置方法,包括棘手的import,因此您还需要阻止对其的访问:

result = eval(expression, {'__builtins__': None}, {})

但这只有在您需要安全性时才会发生,也就是说,如果您允许任何人输入任何表达式。

当然,由于这种方式阻止了所有局部变量的使用,因此您没有任何变量可用,所以要使用它们,您需要仅传递那些应在字典中访问的变量。

vars = {'__builtins__': None, 'x': x}
result = eval(expression, vars, {})

或类似的。


2
使用eval的问题在于表达式=“system.os(rm -rf \)”。如果您以root身份在*nix上运行它,机器就会崩溃。或者如果是Windows等效物,特别是因为有太多人以管理员身份运行Windows。 - Thomas Owens
2
只有当你已经执行了 from os import system 并且没有提供全局和局部目录,我在我的示例中这样做是为了这个原因。 - Lennart Regebro
1
@Thomas:如果你在Linux环境下以root身份运行或者在Windows系统中是管理员,那么这样造成的后果就是你自食其果。 - D.Shawley
1
@D.Shawley:那么对于不以root身份运行且只失去自己文件的可怜用户,他/她应该承受这样的后果吗? - Martin B
那么对于 eval("[2**(10**a) for a in range(100000)]", vars, {}) 呢? - MatthewWilkes
显示剩余5条评论

1

我对Python和任何极其Pythonic的方法都不是特别熟悉,但你可以看一下《设计模式》中定义的解释器模式。它专门用于处理“语言”,而数学表达式确实遵循特定的规则语言。事实上,维基百科上的示例实际上是一个逆波兰计算器的Java实现。


18
他们现在也把解析称为“模式”吗?这一定是计算机科学中使用最过度的词语了... - Noldorin
1
@Thomas:这并不比通用解析更具体。我是说,所有解析都涉及某种“句子”,并且任何像样的解析器都是干净/可理解的,至少在某种程度上。(以递归下降为例)。另外,是s/their/they're :P。 - Noldorin
2
解释器是GOF模式的原始设计。该示例是一个简单正则表达式库的(非常)高级设计。它建议使用AST来描述语言,而不是(也许还包括)用于解析输入。这应该适用于递归下降,但它并没有涵盖如何实现。实际上,我认为这本书中不应该有这个内容 - 你只需要一个正则表达式库和(也许)一个表达式求值库,而不是一个模式。除此之外,就可以使用lex、yacc和其他相关工具了。 - user180247
1
@Noldorin:“所以他们现在也把解析称作‘模式’了吗?”我喜欢你这样说,好像GoF书才在过去几个月里发布。解释器模式不仅仅是解析。它定义了一种经过验证的读取数据并以可用方式结构化的方法。我见过其他尝试解析的结果是一团糟。 - geowa4
1
嗯——我或许需要撤回之前的“不应该是模式的断言”。我一直在想其他的例子。比如XML解析器、嵌套块二进制文件处理程序、声明性事件流处理程序——例如游戏实体AI和复杂可配置GUI控件,... - user180247
显示剩余3条评论

-1

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接