Skip to content

Instantly share code, notes, and snippets.

@bavadim
Created December 2, 2017 18:14
Show Gist options
  • Save bavadim/6c45cf58b380de038818c88486c166cf to your computer and use it in GitHub Desktop.
Save bavadim/6c45cf58b380de038818c88486c166cf to your computer and use it in GitHub Desktop.
import scala.util.control.NoStackTrace
implicit class Formula(strExpr: String) {
private trait Expr {
def evaluate = {
def eval(e: Expr): String = {
def sub(s1: String, s2: String): String = if (s1.endsWith(s2)) s1.dropRight(s2.length) else s1
def prod(s1: String, s2: String): String =
s1.zip(s2).foldLeft("") { case (acc, (str1, str2)) => acc + str1 + str2 } + s2.drop(s1.length) + s1.drop(s2.length)
def div(s1: String, s2: String): String = {
val onlyOdd = s1.zipWithIndex.filter(_._2 % 2 == 0).map(_._1).mkString
if (s1.zipWithIndex.filter(_._2 % 2 == 1).map(_._1).mkString.startsWith(s2))
onlyOdd.take(s2.length) + s1.drop(s2.length * 2)
else s1
}
e match {
case Err => throw new IllegalArgumentException with NoStackTrace
case Term(value) => value
case Sum(a, b) => eval(a) + eval(b)
case Sub(a, b) => sub(eval(a), eval(b))
case Prod(a, b) => prod(eval(a), eval(b))
case Div(a, b) => div(eval(a), eval(b))
}
}
eval(this)
}
}
private case object Err extends Expr
private case class Term(value: String) extends Expr
private case class Var(name: String) extends Expr
private case class Sum(a: Expr, b: Expr) extends Expr
private case class Sub(a: Expr, b: Expr) extends Expr
private case class Prod(a: Expr, b: Expr) extends Expr
private case class Div(a: Expr, b: Expr) extends Expr
private def parse(strExpr: String): Expr = {
val OPERATORS = Set('+','-','/','*','(',')')
def p(str: List[Char], operationsStack: List[Char], argsStack: List[Expr]): Expr = {
def addOpToStack(op: Option[Char], operationsStack: List[Char], argsStack: List[Expr]): (List[Char], List[Expr]) =
(operationsStack.headOption, op) match {
case (_, Some('(')) => ('(' :: operationsStack, argsStack)
case (Some('('), Some(')')) => (operationsStack.tail, argsStack)
case (Some('('), Some(o)) => (o :: operationsStack, argsStack)
case (Some('*'), _) => addOpToStack(op, operationsStack.tail, Prod(argsStack.tail.head, argsStack.head) :: argsStack.tail.tail)
case (Some('/'), _) => addOpToStack(op, operationsStack.tail, Div(argsStack.tail.head, argsStack.head) :: argsStack.tail.tail )
case (Some('+'), Some('+')) => addOpToStack(op, operationsStack.tail, Sum(argsStack.tail.head, argsStack.head) :: argsStack.tail.tail)
case (Some('+'), Some('-')) => addOpToStack(op, operationsStack.tail, Sum(argsStack.tail.head, argsStack.head) :: argsStack.tail.tail)
case (Some('+'), Some(')')) => addOpToStack(op, operationsStack.tail, Sum(argsStack.tail.head, argsStack.head) :: argsStack.tail.tail)
case (Some('+'), None) => addOpToStack(op, operationsStack.tail, Sum(argsStack.tail.head, argsStack.head) :: argsStack.tail.tail)
case (Some('-'), Some('+')) => addOpToStack(op, operationsStack.tail, Sub(argsStack.tail.head, argsStack.head):: argsStack.tail.tail)
case (Some('-'), Some('-')) => addOpToStack(op, operationsStack.tail, Sub(argsStack.tail.head, argsStack.head) :: argsStack.tail.tail)
case (Some('-'), Some(')')) => addOpToStack(op, operationsStack.tail, Sub(argsStack.tail.head, argsStack.head) :: argsStack.tail.tail)
case (Some('-'), None) => addOpToStack(op, operationsStack.tail, Sub(argsStack.tail.head, argsStack.head) :: argsStack.tail.tail)
case (Some(_), Some(o)) => (o :: operationsStack, argsStack)
case (None, Some(o)) => (o :: operationsStack, argsStack)
case _ => (List.empty, argsStack)
}
val operand = str.takeWhile(c => !OPERATORS.contains(c))
val rest = str.drop(operand.length)
(if (operand.isEmpty) argsStack else Term(operand.mkString) :: argsStack, rest) match {
case (operands, Nil) =>
val (ops, args) = addOpToStack(None, operationsStack, operands)
if (ops.nonEmpty && args.length != 1) Err
else args.head
case (operands, op :: tail) =>
val (ops, args) = addOpToStack(Some(op), operationsStack, operands)
p(tail, ops, args)
}
}
p(strExpr.replaceAll("\\s", "").toList, List.empty[Char], List.empty[Expr])
}
def calculate = parse(strExpr).evaluate
}
assert("abc+xyz".calculate == "abcxyz")
assert("xyz + abc".calculate == "xyzabc")
assert("abcxyz - xyz".calculate == "abc")
assert("abc - xyz".calculate == "abc")
assert("abcxyzd - xyz".calculate == "abcxyzd")
assert("abc * xyz".calculate == "axbycz")
assert("abc *xyzps".calculate == "axbyczps")
assert("abcde *xyz".calculate == "axbyczde")
assert("abc / xyz".calculate == "abc")
assert("axbycz / xyz".calculate == "abc")
assert("axbyczps / xyz".calculate == "abcps")
assert("((index - ex) - d) + gst * osr + (an + k + oh) / (n + o)".calculate == "ingosstrakh")
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment