scala中的表达式求值程序(可能有占位符?)

时间:2014-10-28 13:16:02

标签: regex scala

我正在从配置文件中读到这样的内容:

metric1.critical = "<2000 || >20000"
metric1.okay = "=1"
metric1.warning = "<=3000"
metric2.okay = ">0.9 && < 1.1 "
metric3.warning ="( >0.9 && <1.5) || (<500 &&>200)"

我有一个

metric1.value =  //have some value

我的目标是基本上评估

    if(metric1.value<2000 || metric1.value > 20000)
     metric1.setAlert("critical");
    else if(metric1.value=1)
     metric.setAlert("okay");
    //and so on

我对正则表达式并不是很好,所以我会尝试不使用它。我在Scala中编码,想知道现有的库是否可以帮助解决这个问题。也许我需要放置占位符来填补空白然后评估表达式?但是,如何最有效地评估表达式并减少开销?

编辑: 在java中我们如何使用表达式求值程序库我希望我能找到类似于我的代码的东西。也许我可以在配置文件中添加占位符,如“?”这些替换我的metric1.value(读取变量),然后使用评估器? 要么 有人可以为此建议一个好的正则表达式吗? 提前谢谢!

4 个答案:

答案 0 :(得分:6)

这听起来像是要使用解析器组合器库定义自己的语法。

scala类库中内置了一个解析器组合器。由于scala库已经模块化,现在它是一个独立的项目,位于https://github.com/scala/scala-parser-combinators

更新:每个人都在寻找一个概念上类似于scala-parser-combinators的解析器组合库,应该看看fastparse。它非常快,不使用宏。因此它可以作为scala-parser-combinators的替代品。

有一些关于如何在Programming in Scala中使用它的例子,第33章,&#34; Combinator Parsing&#34;。

这是一个小小的语法,ast和评估员,可以帮助你入门。这缺少很多东西,如空格处理,操作符优先级等。您也不应该使用字符串来编码不同的比较运算符。但是我认为有了这个以及Scala编程中的章节,你应该能够找到适合你需求的东西。

import scala.util.parsing.combinator.{JavaTokenParsers, PackratParsers}

sealed abstract class AST
sealed abstract class BooleanExpression extends AST
case class BooleanOperation(op: String, lhs: BooleanExpression, rhs:BooleanExpression) extends BooleanExpression
case class Comparison(op:String, rhs:Constant) extends BooleanExpression
case class Constant(value: Double) extends AST

object ConditionParser extends JavaTokenParsers with PackratParsers {

  val booleanOperator : PackratParser[String] = literal("||") | literal("&&")
  val comparisonOperator : PackratParser[String] = literal("<=") | literal(">=") | literal("==") | literal("!=") | literal("<") | literal(">")
  val constant : PackratParser[Constant] = floatingPointNumber.^^ { x => Constant(x.toDouble) }
  val comparison : PackratParser[Comparison] = (comparisonOperator ~ constant) ^^ { case op ~ rhs => Comparison(op, rhs) }
  lazy val p1 : PackratParser[BooleanExpression] = booleanOperation | comparison
  val booleanOperation = (p1 ~ booleanOperator ~ p1) ^^ { case lhs ~ op ~ rhs => BooleanOperation(op, lhs, rhs) }
}

object Evaluator {

  def evaluate(expression:BooleanExpression, value:Double) : Boolean = expression match {
    case Comparison("<=", Constant(c)) => value <= c
    case Comparison(">=", Constant(c)) => value >= c
    case Comparison("==", Constant(c)) => value == c
    case Comparison("!=", Constant(c)) => value != c
    case Comparison("<", Constant(c)) => value < c
    case Comparison(">", Constant(c)) => value > c
    case BooleanOperation("||", a, b) => evaluate(a, value) || evaluate(b, value)
    case BooleanOperation("&&", a, b) => evaluate(a, value) && evaluate(b, value)
  }
}

object Test extends App {

  def parse(text:String) : BooleanExpression = ConditionParser.parseAll(ConditionParser.p1, text).get

  val texts = Seq(
    "<2000",
    "<2000||>20000",
    "==1",
    "<=3000",
    ">0.9&&<1.1")

  val xs = Seq(0.0, 1.0, 100000.0)

  for {
    text <- texts
    expression = parse(text)
    x <- xs
    result = Evaluator.evaluate(expression, x)
  } {
    println(s"$text $expression $x $result")
  }
}

答案 1 :(得分:2)

Scala内置了可以使用的Interpreter库。该库提供了与许多其他语言中的eval()类似的功能。您可以将Scala代码段作为String传递给.interpret方法,它将对其进行评估。

import scala.tools.nsc.{ Interpreter, Settings }

val settings = new Settings   
settings.usejavacp.value = true
val in = new Interpreter(settings) 
val lowerCritical = "<2000" // set the value from config
val value = 200   
in.interpret(s"$value $lowerCritical")                    //> res0: Boolean = true
val value1 = 20000                               //> value1  : Int = 20000
in.interpret(s"$value1 $lowerCritical")          //> res1: Boolean = false

答案 2 :(得分:2)

您想为此使用实际的解析器。

大多数答案都建议使用Scala的解析器组合器,如果有点过时,这是一个非常有效的选择。

我建议使用Parboiled2,这是另一个解析器组合实现,具有作为Scala宏编写的独特优势 - 不会过于技术化,这意味着您的解析器是在编译时而不是运行时生成的,这可以产生显着的性能改进。一些基准测试的Parboiled2速度是Scala解析器组合器的200倍。

由于解析器组合器现在处于单独的依赖关系中(从2.11开始,我相信),因此没有理由将它们更喜欢Parboiled2。

答案 3 :(得分:0)

我最近遇到了同样的问题,最后我编写了自己的表达式评估库scalexpr。它是一个简单的库,但它可以验证/评估与问题中的表达式类似的表达式。你可以这样做:

val ctx = Map("id" -> 10L, "name" -> "sensor1")
val parser = ExpressionParser()
val expr = parser.parseBooleanExpression(""" id == 10L || name == "sensor1" """).get
println(expr3.resolve(ctx3)) // prints true

如果你不想使用这个库,我推荐使用fastparse解析器...它比解析器组合器快得多,比parboiled慢一点,但比两者都容易使用。 / p>