Created
December 2, 2017 18:14
-
-
Save bavadim/6c45cf58b380de038818c88486c166cf to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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