面向Java开发人员的Scala指南 - 构建计算器,第3部分2011-01-30Ted Neward欢迎勇于探索的读者回到我们的系列文章中!本月继续探索 Scala 的语言和库支持,我们将改造一下计算器 DSL 并最终 “完成它”。DSL 本身有点简单 — 一个简单的计算器,目前为止只支持 4 个基本数学运算符。但要记住,我们的目标是创建一些可扩展的、灵活的对象,并且以后可以轻松增强它们以支持新的功能。继续上次的讨论……说明一下,目前我们的 DSL 有点零乱。我们有一个抽象语法树(Abstract Syntax Tree ),它由大量 case 类组成……清单 1. 后端(AST)package com.tedneward.calcdsl { // ... private[calcdsl] abstract class Expr private[calcdsl] case class Variable(name : String) extends Expr private[calcdsl] case class Number(value : Double) extends Expr private[calcdsl] case class UnaryOp(operator : String, arg : Expr) extends Expr private[calcdsl] case class BinaryOp(operator : String, left : Expr, right : Expr) extends Expr }……对此我们可以提供类似解释器的行为,它能最大限度地简化数学表达式……清单 2. 后端(解释器)package com.tedneward.calcdsl { // ... object Calc { def simplify(e: Expr): Expr = { // first simplify the subexpressions val simpSubs = e match { // Ask each side to simplify case BinaryOp(op, left, right) => BinaryOp(op, simplify(left), simplify(right)) // Ask the operand to simplify case UnaryOp(op, operand) => UnaryOp(op, simplify(operand)) // Anything else doesn"t have complexity (no operands to simplify) case _ => e } // now simplify at the top, assuming the components are already simplified def simplifyTop(x: Expr) = x match { // Double negation returns the original value case UnaryOp("-", UnaryOp("-", x)) => x
// Positive returns the original value case UnaryOp("+", x) => x
// Multiplying x by 1 returns the original value case BinaryOp("*", x, Number(1)) => x
// Multiplying 1 by x returns the original value case BinaryOp("*", Number(1), x) => x
// Multiplying x by 0 returns zero case BinaryOp("*", x, Number(0)) => Number(0)
// Multiplying 0 by x returns zero case BinaryOp("*", Number(0), x) => Number(0)
// Dividing x by 1 returns the original value case BinaryOp("/", x, Number(1)) => x
// Dividing x by x returns 1 case BinaryOp("/", x1, x2) if x1 == x2 => Number(1)
// Adding x to 0 returns the original value case BinaryOp("+", x, Number(0)) => x
// Adding 0 to x returns the original value case BinaryOp("+", Number(0), x) => x
// Anything else cannot (yet) be simplified case e => e } simplifyTop(simpSubs) }
def evaluate(e : Expr) : Double = { simplify(e) match { case Number(x) => x case UnaryOp("-", x) => -(evaluate(x)) case BinaryOp("+", x1, x2) => (evaluate(x1) + evaluate(x2)) case BinaryOp("-", x1, x2) => (evaluate(x1) - evaluate(x2)) case BinaryOp("*", x1, x2) => (evaluate(x1) * evaluate(x2)) case BinaryOp("/", x1, x2) => (evaluate(x1) / evaluate(x2)) } } } }