简体   繁体   中英

Scala - string of algebraic operations

I have a string representing some basic algebraic operations, for example:

 "42 * 67 + 4"

I would like a function of type

 function (operation: String) : Int = {}

such that it takes a string of algebraic operations and returns the actual final value, in this case: 2818

I would know how to extract the numbers from such a string but I do not have clear how to extract the mathematical operations such as '+', '-', '*', '/' and actually compute them.

It's fairly simple to implement the Shunting-yard algorithm to do this sort of calculations without any external libraries:

def calculate(operation: String): Int = {
  var results: List[Int] = Nil
  var operators: List[String] = Nil

  def precedence(operator: String) = operator match {
    case "+" | "-" => 0
    case "*" | "/" => 1
  }

  def execute(operator: String): Unit = {
    (results, operator) match {
      case (x :: y :: rest, "+") => results = (y + x) :: rest
      case (x :: y :: rest, "-") => results = (y - x) :: rest
      case (x :: y :: rest, "*") => results = (y * x) :: rest
      case (x :: y :: rest, "/") => results = (y / x) :: rest
      case (_, _) => throw new RuntimeException("Not enough arguments")
    }
  }

  for (term <- "[1-9][0-9]*|[-+/*]".r.findAllIn(operation)) {
    util.Try(term.toInt) match {
      case util.Success(number) => results ::= number
      case _ =>
        val (operatorsToExecute, rest) = 
          operators.span(op => precedence(op) >= precedence(term))
        operatorsToExecute foreach execute
        operators = term :: rest
    }
  }
  operators foreach execute

  results match {
    case res :: Nil => res
    case _ => throw new RuntimeException("Too many arguments")
  }
}

This uses integer division:

scala> calculate("3 / 2")
res0: Int = 1

And has correct precedence of addition and multiplication:

scala> calculate("2 + 2 * 2")
res1: Int = 6

Support of:

  • more kinds of operations,
  • parentheses, eg 2 * (2 + 2) ,
  • floating-point calculations,
  • better testing of the formula content (currently it simply ignores all characters except numbers and operators)
  • not throwing errors (for example, returning Try[Int] , Option[Int] , etc. instead of the current behaviour of returning a bare Int or throwing an error)

is left as an exercise to the reader.

For more complex things, of course it would be better to use scala-parser-combinators or some third-party parsing library as proposed in other answers.

So, @Gábor Bakos posted his joking comment, while I was still composing and testing my joking answer, but I'll post it anyway.

Note: it works. Sometimes. A bit. Note2: it's a joke!

def function(operation: String) = {
  val js = new javax.script.ScriptEngineManager().getEngineByName("JavaScript")
  js.eval(operation) match { case i: Integer => i.asInstanceOf[Int] }
}

function("42 * 67 + 4")
// => 2818 : Int

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM