如何在Scala中转换为RPN(反向波兰符号)?

时间:2019-07-14 06:04:33

标签: algorithm scala collections functional-programming rpn

我试图在Scala中将公式转换为RPN(反向波兰表示法)。

RPN:https://en.wikipedia.org/wiki/Reverse_Polish_notation

但是我无法编写任何代码。

object RPNCalclator {

  def convertToRPN(expr: String): String = {
    ???
  }

  def calcRPN(expr: String): Double = {
    ???
  }

  def main(args: Array[String]): Unit = {
    val expr = "4 * ( 8 + 4 + 3 )"
    val rpn = convertToRPN(expr) // " 4 8 4 3 + + *"
    println(calcRPN(rpn))
  }
}

我想弄清楚如何编写convertToRPN代码。

例如,

  • 4 + 5-> 4 5 +
  • 1 + 2 * 3 + 4-> 1 2 3 * 4 + +
  • ( 1 + 2 ) * ( 3 + 4 )-> 1 2 + 3 4 + *

1 个答案:

答案 0 :(得分:1)

您确实需要一个完整的语法解析器,但这有点脏。

def convertToRPN(expr :String, ops :String = "") :String = {
  val str = expr.strip
  if (str.isEmpty) ops.mkString(" ")         //done?
  else if (str.head == '(') {                //start of parentheses
    val spltAt = str.iterator.scanLeft(0){case (lvl,c) =>
      if (c=='(') lvl+1 else if (c==')') lvl-1 else lvl
    }.drop(1).indexOf(0)
    val (paren, s) = str.splitAt(spltAt)
    s"${convertToRPN(paren.tail)} ${convertToRPN(s.tail, ops)}"
  } else {
    val (token, s) = str.span(_ != ' ')
    if (util.Try(token.toDouble).isSuccess)  //is number
      s"$token ${convertToRPN(s, ops)}"
    else if (token matches "[*/]")           //is higher precedence op
      convertToRPN(s, s"$token$ops")
    else if (token matches "[-+]") {         //is lower precedence op
      ops.headOption.fold(convertToRPN(s, s"$token$ops")){
        case '-'|'+' => convertToRPN(s, s"$token$ops")
        case _ =>
          s"${ops.head} ${convertToRPN(s, s"$token${ops.tail}")}"
      }
    } else throw new Error(s"unable to parse token: $token")
  }
}

是的,我知道,对于“ quick-n-dirty”来说,这是很多代码。它是递归的,但不是尾递归的。 (那会有点脏。)

这应该适用于大多数数字格式,包括负数,但是它确实依赖于空格分隔符,因此不会解析诸如1+2之类的东西。

与此相比,

calcRPN()应该相对容易。