I'm learning Scala by working the exercises from the book "Scala for the Impatient". One question asks:
/**
* Q8: Extends the tree in the preceding exercise so that each non-leaf node stores an operator in addition to
* the child nodes. Then write a function `eval` that computes the value. For example, the tree
*
* +
* * 2 -
* 3 8 5
*
* has a value (3 * 8) + 2 + (-5) = 21
*
*/
My solution is as follows. Can you improve on it? In particular, I'm wondering if there's a way to directly match against the functions instead of the method names. For example, if I could write something like the following imaginary statement
case ExprTreeNode(f, children @ _*) if (f == Int.+ || f == Int.-) => children.foldLeft(0) { (acc, elem) => eval(elem) f acc }
then I could combine the +
and -
cases. Same goes for *
and /
.
sealed abstract class ExpressionTree
case class ExprTreeLeaf(value: Int) extends ExpressionTree
case class ExprTreeNode(op: String, children: ExpressionTree*) extends ExpressionTree
def eval(expr: ExpressionTree): Int = expr match {
case ExprTreeNode("+", children @ _*) => children.foldLeft(0) { _ + eval(_) }
case ExprTreeNode("*", children @ _*) => children.foldLeft(1) { _ * eval(_) }
case ExprTreeNode("/", children @ _*) => children.foldLeft(1) { (acc, elem) => eval(elem) / acc }
case ExprTreeNode("-", child) => eval(child).unary_- // Order matters here, 'children @ _*' would match 1 child
case ExprTreeNode("-", children @ _*) => children.foldLeft(0) { (acc, elem) => eval(elem) - acc }
case leaf: ExprTreeLeaf => leaf.value
}
Test case:
"Method eval" should "evaluate an expression tree" in {
val expr = ExprTreeNode("+", ExprTreeNode("*", ExprTreeLeaf(3), ExprTreeLeaf(8)), ExprTreeLeaf(2), ExprTreeNode("-", ExprTreeLeaf(5)))
eval(expr) should be(21)
}
"+"
,"*"
, ... are not functions, justString
s. You can create aMap
/Function1
from thoseString
s to the functions you want and use that in fold (also for the neutral element).ExprTreeNode
as follows:case class ExprTreeNode(op: (Int, Int) => Int, children: ExpressionTree*) extends ExpressionTree
A method map doesn't buy me much value.