Requesting for a code review for a scala implementation.
Problem
Given an expression string made of numbers and operators +,-,/,*
, break it in a list of Integer or String.
Example
"1+2+3" --> List(1, "+", 2 , "+", 3)
"+1++2++3" --> List(1, "+", 2 , "+", 3) //unary +
"1+-2+-3" --> List(1, "+", -2 , "+", -3)//unary -
"1--2--3" --> List(1, "-", -2 , "-", -3)
"-2-3*-4/-6" --> List(-2, "-", 3 , "*", -4, "/", 6)
Error checks are not needed for the valid expression, i.e. expression like *, 2+1 or // wont be passed.
Solution
I implemented the solution in Scala 2.13 . Using Either to store Int or String in the result. Wanted to check if I got algo correct for positive cases.
def breakExpression(s: String): List[Either[Int, String]] = {
def isValidOperator(op: String): Boolean = List("+", "-", "/", "*").contains(op)
def isValidNumber(num:String): Boolean = num.forall(_.isDigit)
def breakByOperator(expression:String,operator: Char): List[String] = {
expression.split(operator).filter(_.nonEmpty).foldLeft(List.empty[String]) { case (res, e) =>
res :+ e :+ operator.toString
}.init
}
def breakByUnaryMinusOperator(expression: String): List[Int] =
expression.split('-').foldLeft((List.empty[(String,Int)], 1)) { case ((resInProgress, multiplier), exp) =>
if (exp.isEmpty) (resInProgress, -1)
else (resInProgress :+ (exp, multiplier), 1)
}._1.map { x => x._1.toInt * x._2 }
List('+', '/', '*').foldLeft(List(s)) { case (expressions, op) =>
expressions flatMap { e =>
if (isValidOperator(e) || isValidNumber(e)) List(e)
else breakByOperator(e, op)
}
}.map {
case ss if isValidNumber(ss) => Left(ss.toInt)
case ss => Right(ss)
}.flatMap {
case Left(s) => List(Left(s))
case Right(s) if isValidOperator(s) => List(Right(s))
case Right(s) =>
breakByUnaryMinusOperator(s).foldLeft(List.empty[Either[Int, String]]) { case (res, number) =>
res :+ Left(number) :+ Right("-")
}.init
}
}
Testing
Test cases
println(breakExpression("2+34/6"))
println( "2-3*4/6" , " ---> " , breakExpression("2-3*4/6"))
println("2-3*-4/-6", " ---> ", breakExpression("2-3*-4/-6"))
println("-2-3*-4/-6", " ---> ", breakExpression("-2-3*-4/-6"))
println("1+-2--3", " ---> ", breakExpression("1+-2--3"))
I get following result
List(Left(2), Right(+), Left(34), Right(/), Left(6))
(2-3*4/6, ---> ,List(Left(2), Right(-), Left(3), Right(*), Left(4), Right(/), Left(6)))
(2-3*-4/-6, ---> ,List(Left(2), Right(-), Left(3), Right(*), Left(-4), Right(/), Left(-6)))
(-2-3*-4/-6, ---> ,List(Left(-2), Right(-), Left(3), Right(*), Left(-4), Right(/), Left(-6)))
(1+-2--3, ---> ,List(Left(1), Right(+), Left(-2), Right(-), Left(-3)))
Followup Question: What is the best way to introduce error handling for badly formed string expressions.
-
(or+
) can't be an operator, but must be the sign of a following number. \$\endgroup\$"-2-3*-4/-6" --> List(-2, "-", 3 , "*", -4, "/", 6)
Shouldn't the resulting list have-6
as the last item? \$\endgroup\$6----4
would be legal with a meaning of6-(-(-(-4)))
and a resulting value of10
. \$\endgroup\$