我从维基书籍页面here获得了以下代码。它解析数学表达式,它对我正在处理的代码非常有效。虽然有一个问题,但当我开始在我的表达式中添加括号层时,程序会显着减慢,在某些时候崩溃我的计算机。它与我检查的运算符数量有关,运算符越多,我可以解析的括号越少。无论如何要绕过或解决这个瓶颈?
非常感谢任何帮助。
import Text.ParserCombinators.ReadP
-- slower
operators = [("Equality",'='),("Sum",'+'), ("Product",'*'), ("Division",'/'), ("Power",'^')]
-- faster
-- ~ operators = [("Sum",'+'), ("Product",'*'), ("Power",'^')]
skipWhitespace = do
many (choice (map char [' ','\n']))
return ()
brackets p = do
skipWhitespace
char '('
r <- p
skipWhitespace
char ')'
return r
data Tree op = Apply (Tree op) (Tree op) | Branch op (Tree op) (Tree op) | Leaf String deriving Show
leaf = chainl1 (brackets tree
+++ do
skipWhitespace
s <- many1 (choice (map char "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789.-[]" ))
return (Leaf s))
(return Apply)
tree = foldr (\(op,name) p ->
let this = p +++ do
a <- p +++ brackets tree
skipWhitespace
char name
b <- this
return (Branch op a b)
in this)
(leaf +++ brackets tree)
operators
readA str = fst $ last $ readP_to_S tree str
main = do loop
loop = do
-- ~ try this
-- ~ (a+b+(c*d))
str <- getLine
print $ last $ readP_to_S tree str
loop
答案 0 :(得分:2)
这是回溯中的经典问题(或者是并行解析,它们基本上是一回事)....回溯随着输入的大小呈指数增长(最坏),所以解析某些东西的时间会突然爆发。在实践中,回溯在大多数输入的语言解析中都可以正常工作,但是会使用递归中缀运算符表示法进行爆炸。您可以通过考虑可以解析多少可能的方法(使用组合&amp;和%运算符)来了解原因:
a & b % c & d
可以解析为
a & (b % (c & d))
a & ((b % c) & d)
(a & (b % c)) & d
((a & b) % c) & d
这就像2 ^(n-1)一样增长。对此的解决方案是在解析中更早地添加一些运算符预置信息,并抛弃除敏感案例之外的所有信息....您将需要一个额外的堆栈来保存挂起的运算符,但是您总是可以通过O中的中缀运算符表达式(1)。
像yacc这样的LR解析器为你做这个....使用解析器组合器你需要手动完成。在parsec中,有一个带有buildExpressionParser函数的Expr包,可以为你构建它。