0

If I have a string that has an arithmetic expression with paranthesis like:

((4 * 7) / 2) - 7

How do I evaluate it automatically? In particular with Kotlin. I heard that you need to make a parser, so how can I do so in Kotlin and have all the necessary basic operations in the example as such?

5
  • 1
    Shunting yard algorithm is a good starting place. Commented Mar 21, 2022 at 15:47
  • Practically the same question was asked last week. And again the day before that… Commented Mar 21, 2022 at 16:51
  • @gidds It seems so, but you have linked to answers that eventually lead to a question answered regarding Java and the solution being built in Javascript. I believe my question is valid enough given my requirements, I want to know how to do this in vanilla Kotlin. Commented Mar 21, 2022 at 20:35
  • 1
    @Jiehfeng Fair enough. (Though I think it can still be helpful to link to other questions and explain why this isn't a duplicate.) I'm curious, though (since it's not the first time I've seen very similar questions posted in quick succession). Do you know of any connection with the previous questions, or is it just an interesting coincidence? Commented Mar 21, 2022 at 20:45
  • @gidds Noted, I'll see what I can do tomorrow. As for those questions, certainly haha. It seems they're from the same university as I am, there's a coursework due tomorrow so the timing and questions have the uncanny resemblance to the coursework specification, especially with how new they are to questioning and how recent their accounts are... Apologies about this, this is most definitely the last given the deadline. Commented Mar 21, 2022 at 20:51

2 Answers 2

8
fun evaluate(str: String): Double {

  data class Data(val rest: List<Char>, val value: Double)

  return object : Any() {

    fun parse(chars: List<Char>): Double {
      return getExpression(chars.filter { it != ' ' })
        .also { if (it.rest.isNotEmpty()) throw RuntimeException("Unexpected character: ${it.rest.first()}") }
        .value
    }

    private fun getExpression(chars: List<Char>): Data {
      var (rest, carry) = getTerm(chars)
      while (true) {
        when {
          rest.firstOrNull() == '+' -> rest = getTerm(rest.drop(1)).also { carry += it.value }.rest
          rest.firstOrNull() == '-' -> rest = getTerm(rest.drop(1)).also { carry -= it.value }.rest
          else                      -> return Data(rest, carry)
        }
      }
    }

    private fun getTerm(chars: List<Char>): Data {
      var (rest, carry) = getFactor(chars)
      while (true) {
        when {
          rest.firstOrNull() == '*' -> rest = getTerm(rest.drop(1)).also { carry *= it.value }.rest
          rest.firstOrNull() == '/' -> rest = getTerm(rest.drop(1)).also { carry /= it.value }.rest
          else                      -> return Data(rest, carry)
        }
      }
    }

    private fun getFactor(chars: List<Char>): Data {
      return when (val char = chars.firstOrNull()) {
        '+'              -> getFactor(chars.drop(1)).let { Data(it.rest, +it.value) }
        '-'              -> getFactor(chars.drop(1)).let { Data(it.rest, -it.value) }
        '('              -> getParenthesizedExpression(chars.drop(1))
        in '0'..'9', '.' -> getNumber(chars) // valid first characters of a number
        else             -> throw RuntimeException("Unexpected character: $char")
      }
    }

    private fun getParenthesizedExpression(chars: List<Char>): Data {
      return getExpression(chars)
        .also { if (it.rest.firstOrNull() != ')') throw RuntimeException("Missing closing parenthesis") }
        .let { Data(it.rest.drop(1), it.value) }
    }

    private fun getNumber(chars: List<Char>): Data {
      val s = chars.takeWhile { it.isDigit() || it == '.' }.joinToString("")
      return Data(chars.drop(s.length), s.toDouble())
    }

  }.parse(str.toList())

}

val expression = "((4 * 7) / 2) - 7"

val result = evaluate(expression)

println(result)   // Output: 7.0
Sign up to request clarification or add additional context in comments.

2 Comments

I’ve seen a couple of your answers like this and am just curious. What is the purpose of creating an anonymous object to wrap all your functions? It seems like it just creates extra nesting and I don’t see the benefit.
Because of the circular calls like getExpression -> getTerm -> getFactor -> getParenthesizedExpression -> getExpression. Since there are no forward declarations in Kotlin I use this kind of solution to 'squeeze' everything into a function.
-1

Look up "Math parser" on google. These are various dependencies you can add that allow you to then parse string input into mathematical formulas.

1 Comment

This does not provide an answer to the question. Once you have sufficient reputation you will be able to comment on any post; instead, provide answers that don't require clarification from the asker. - From Review

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.