您的位置:首页 > 编程语言 > Java开发

面向 Java 开发人员的 Scala 指南:构建计算器,第 2 部分

2008-11-25 15:02 696 查看
特定领域语言(Domain-specific
languages,DSL)已经成为一个热门话题;很多函数性语言之所以受欢迎,主要是因为它们可以用于构建 DSL。有鉴于此,在 面向 Java 开发人员的 Scala 指南 系列的最后一篇文章中,Ted Neward
继续讨论一个简单的计算器 DSL,以展示函数性语言在构建“外部”DSL 的强大功能,并在此过程中解决将文本输入转换成用于解释的 AST
的问题。为了解析文本输入,并将它转换成上一篇文章中解释器使用的树结构,Ted 引入了 解析器组合子(parser
combinator)
,这是一个专门为这项任务设计的标准 Scala 库。(在 上一篇文章 中,我们构建了一个计算器解析器和 AST)。
回忆一下我们的英雄所处的困境:在试图创建一个 DSL(这里只不过是一种非常简单的计算器语言)时,他创建了包含可用于该语言的各种选项的树结构:

二进制加/减/乘/除运算符

一元反运算符

数值

它背后的执行引擎知道如何执行那些操作,它甚至有一个显式的优化步骤,以减少获得结果所需的计算。

最后的 代码 是这样的:

清单 1. 计算器 DSL: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

object Calc
{
/**
* Function to simplify (a la mathematic terms) expressions
*/
def simplify(e : Expr) : Expr =
{
e match {
// Double negation returns the original value
case UnaryOp("-", UnaryOp("-", x)) => simplify(x)

// Positive returns the original value
case UnaryOp("+", x) => simplify(x)

// Multiplying x by 1 returns the original value
case BinaryOp("*", x, Number(1)) => simplify(x)

// Multiplying 1 by x returns the original value
case BinaryOp("*", Number(1), x) => simplify(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)) => simplify(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)) => simplify(x)

// Adding 0 to x returns the original value
case BinaryOp("+", Number(0), x) => simplify(x)

// Anything else cannot (yet) be simplified
case _ => e
}
}

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))
}
}
}
}
前一篇文章的读者应该还记得,我布置了一个挑战任务,要求改进优化步骤,进一步在树中进行简化处理,而不是像清单 1 中的代码那样停留在最顶层。Lex
Spoon 发现了我认为是最简单的优化方法:首先简化树的 “边缘”(每个表达式中的操作数,如果有的话),然后利用简化的结果,再进一步简化顶层的表达式,如清单 2
所示:

清单 2. 简化、再简化

本文转自IBM Developerworks中国

请点击此处查看全文
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: