【发布时间】:2012-10-04 06:12:16
【问题描述】:
以下代码示例在解析嵌套在括号中的表达式时由于堆栈溢出而崩溃。
解析器组合器是标准库的一部分。有没有办法利用图书馆避免这种情况?
(我不是在问它崩溃的原因,而是在问处理标准库的正确方法。)
解析: (((((((((... 1 + 1 ...)))))))))
代码:
import scala.util.parsing.combinator.syntactical.StandardTokenParsers
object ArithmeticParser1 extends StandardTokenParsers {
lexical.delimiters ++= List("(", ")", "+", "-", "*", "/")
val reduceList: Int ~ List[String ~ Int] => Int = {
case i ~ ps => (i /: ps)(reduce)
}
def reduce(x: Int, r: String ~ Int) = (r: @unchecked) match {
case "+" ~ y => x + y
case "-" ~ y => x - y
case "*" ~ y => x * y
case "/" ~ y => x / y
}
def expr : Parser[Int] = term ~ rep ("+" ~ term | "-" ~ term) ^^ reduceList
def term : Parser[Int] = factor ~ rep ("*" ~ factor | "/" ~ factor) ^^ reduceList
def factor: Parser[Int] = "(" ~> expr <~ ")" | numericLit ^^ (_.toInt)
def main(args: Array[String]) {
val s = scala.io.Source.fromFile(args(0)).mkString
val tokens = new lexical.Scanner(s)
println(s)
println(phrase(expr)(tokens))
}
}
【问题讨论】:
-
我无法重现崩溃。嵌套括号多少层让程序抛出异常?
-
我也试过了,并且(使用默认的 jvm 堆栈大小)在遇到堆栈溢出之前必须上升到 3500 级(= 括号对数)!这为现实世界的表达留下了相当大的空间......@buerger:我很想知道你是否在更合理的水平上发生了堆栈溢出,否则哪个用例需要那么多嵌套。
-
改进解析器库的一种方法是使其使用显式堆栈。任何级别的嵌套都是合理的。但没有任何修改库的假设方式是合理的。
-
理论上可以,可以解析的表达式的复杂度应该没有限制。在实践中,计算中的任何事物都受到限制,无论是时间(有时计算时间太长而无法实际使用)或内存(这绝对是有界的)。需要明确的是,如果您尝试使其编译表达式
((((((((... 1 + 1 ...))))))))),嵌套级别为3500,则scala编译器本身将发生堆栈溢出。试试吧。 -
并且使用显式堆栈肯定会极大地帮助事情,但在某一时刻,您的堆栈将再次占用整个内存,您会碰壁。我知道我可能看起来很迂腐,但我想说的是,你一定需要限制。所以真正唯一真正的问题是:3500 的水平是不可接受的限制吗?
标签: parsing scala recursion stack-overflow