面向 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
和解释器
前一篇文章的读者应该还记得,我布置了一个挑战任务,要求改进优化步骤,进一步在树中进行简化处理,而不是像清单 1 中的代码那样停留在最顶层。Lex
Spoon 发现了我认为是最简单的优化方法:首先简化树的 “边缘”(每个表达式中的操作数,如果有的话),然后利用简化的结果,再进一步简化顶层的表达式,如清单 2
所示:
清单 2. 简化、再简化
本文转自IBM Developerworks中国
请点击此处查看全文
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)) } } } } |
Spoon 发现了我认为是最简单的优化方法:首先简化树的 “边缘”(每个表达式中的操作数,如果有的话),然后利用简化的结果,再进一步简化顶层的表达式,如清单 2
所示:
清单 2. 简化、再简化
本文转自IBM Developerworks中国
请点击此处查看全文
相关文章推荐
- 面向 Java 开发人员的 Scala 指南: 构建计算器,第 1 部分
- 面向 Java 开发人员的 Scala 指南:包和访问修饰符
- 面向 Java 开发人员的 Scala 指南:集合类型
- 面向 Java 开发人员的 Scala 指南: 面向对象的函数编程
- 面向 Java 开发人员的 Scala 指南: 深入了解 Scala 并发性
- 面向 Java 开发人员的 Scala 指南: 深入了解 Scala 并发性2
- 面向 Java 开发人员的 Scala 指南:关于特征和行为
- 面向 Java 开发人员的 Scala 指南: 深入了解 Scala 并发性
- 面向 Java 开发人员的 Scala 指南系列
- 面向 Java 开发人员的 Scala 指南: 面向对象的函数编程
- (转载)面向 Java 开发人员的 Scala 指南: 类操作
- 面向 Java 开发人员的 Scala 指南: 深入了解 Scala 并发性
- 面向 Java 开发人员的 Scala 指南: 深入了解 Scala 并发性
- 面向 Java 开发人员的 Scala 指南: 类操作
- 面向 Java 开发人员的 db4o 指南: 事务、分布和安全性
- 面向Java开发人员的Ajax: 构建动态的Java应用程序
- 面向Java开发人员的Flex开发指南
- cobol声明变量面向Java开发人员的Flex开发指南
- 面向 Java 开发人员的 db4o 指南: 结构化对象和集合
- 面向 PHP 开发人员的 XML,第 2 部分: 高级 XML 解析技术