加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 综合聚焦 > 服务器 > 安全 > 正文

scala – 算术表达式语法和解析器

发布时间:2020-12-16 08:55:00 所属栏目:安全 来源:网络整理
导读:最近我一直在为算术表达式寻找一个合适的语法,但发现只有琐碎的语法,例如忽略了pow(…,…).然后我自己尝试了,但有时它没有按照预期工作.例如,我错过了允许一元 – 在表达式前面并修复它.也许有人可以看看我目前的方法并改进它.此外,我认为其他人可以利用,因
最近我一直在为算术表达式寻找一个合适的语法,但发现只有琐碎的语法,例如忽略了pow(…,…).然后我自己尝试了,但有时它没有按照预期工作.例如,我错过了允许一元 – 在表达式前面并修复它.也许有人可以看看我目前的方法并改进它.此外,我认为其他人可以利用,因为能够解析算术表达式是一项常见任务.

import scala.math._
import scala.util.parsing.combinator._
import scala.util.Random

class FormulaParser(val constants: Map[String,Double] = Map(),val userFcts: Map[String,String => Double] = Map(),random: Random = new Random) extends JavaTokenParsers {
  require(constants.keySet.intersect(userFcts.keySet).isEmpty)
  private val allConstants = constants ++ Map("E" -> E,"PI" -> Pi,"Pi" -> Pi) // shouldn′t be empty
  private val unaryOps: Map[String,Double => Double] = Map(
   "sqrt" -> (sqrt(_)),"abs" -> (abs(_)),"floor" -> (floor(_)),"ceil" -> (ceil(_)),"ln" -> (math.log(_)),"round" -> (round(_)),"signum" -> (signum(_))
  )
  private val binaryOps1: Map[String,(Double,Double) => Double] = Map(
   "+" -> (_+_),"-" -> (_-_),"*" -> (_*_),"/" -> (_/_),"^" -> (pow(_,_))
  )
  private val binaryOps2: Map[String,Double) => Double] = Map(
   "max" -> (max(_,_)),"min" -> (min(_,_))
  )
  private def fold(d: Double,l: List[~[String,Double]]) = l.foldLeft(d){ case (d1,op~d2) => binaryOps1(op)(d1,d2) } 
  private implicit def map2Parser[V](m: Map[String,V]) = m.keys.map(_ ^^ (identity)).reduceLeft(_ | _)
  private def expression:  Parser[Double] = sign~term~rep(("+"|"-")~term) ^^ { case s~t~l => fold(s * t,l) }
  private def sign:        Parser[Double] = opt("+" | "-") ^^ { case None => 1; case Some("+") => 1; case Some("-") => -1 }
  private def term:        Parser[Double] = longFactor~rep(("*"|"/")~longFactor) ^^ { case d~l => fold(d,l) }
  private def longFactor:  Parser[Double] = shortFactor~rep("^"~shortFactor) ^^ { case d~l => fold(d,l) }
  private def shortFactor: Parser[Double] = fpn | sign~(constant | rnd | unaryFct | binaryFct | userFct | "("~>expression<~")") ^^ { case s~x => s * x }
  private def constant:    Parser[Double] = allConstants ^^ (allConstants(_))
  private def rnd:         Parser[Double] = "rnd"~>"("~>fpn~","~fpn<~")" ^^ { case x~_~y => require(y > x); x + (y-x) * random.nextDouble } | "rnd" ^^ { _ => random.nextDouble }
  private def fpn:         Parser[Double] = floatingPointNumber ^^ (_.toDouble) 
  private def unaryFct:    Parser[Double] = unaryOps~"("~expression~")" ^^ { case op~_~d~_ => unaryOps(op)(d) }
  private def binaryFct:   Parser[Double] = binaryOps2~"("~expression~","~expression~")" ^^ { case op~_~d1~_~d2~_ => binaryOps2(op)(d1,d2) }
  private def userFct:     Parser[Double] = userFcts~"("~(expression ^^ (_.toString) | ident)<~")" ^^ { case fct~_~x => userFcts(fct)(x) }
  def evaluate(formula: String) = parseAll(expression,formula).get
}

所以可以评估以下内容:

val formulaParser = new FormulaParser(
    constants = Map("radius" -> 8D,"height" -> 10D,"c" -> 299792458,// m/s
                    "v" -> 130 * 1000 / 60 / 60,// 130 km/h in m/s
                    "m" -> 80),userFcts  = Map("perimeter" -> { _.toDouble * 2 * Pi } ))

println(formulaParser.evaluate("2+3*5")) // 17.0
println(formulaParser.evaluate("height*perimeter(radius)")) // 502.6548245743669
println(formulaParser.evaluate("m/sqrt(1-v^2/c^2)"))  // 80.00000000003415

任何改进建议?我是否使用正确的语法,或者只是一个时间问题,直到用户键入一个无法解析的有效(相对于我提供的函数)算术表达式?
(操作符优先权是什么?)

解决方法

为了获得更好的性能,我建议在定义解析器时使用private lazy val而不是private def.否则,只要解析器是引用,就会再次创建它.

好的代码BTW.

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读