设计模式-解释器模式(Interpreter)

设计模式-解释器模式(Interpreter)

    • 一、解释器模式概述
      • 1.1 什么是解释器模式
      • 1.2 简单实现解释器模式
      • 1.3 使用解释器模式的注意事项
    • 二、解释器模式的用途
    • 三、解释器模式实现方式
      • 3.1 基于递归下降实现解释器模式
      • 3.2 基于LL(1)文法实现解释器模式
      • 3.3 基于ANTLR的工具实现解释器模式
      • 3.4 基于Lambda表达式实现解释器模式

一、解释器模式概述

1.1 什么是解释器模式

解释器模式是一种行为设计模式,它将一个表达式转化为一个由其他对象组成的树形结构,然后通过遍历该树来求解表达式的值。

在解释器模式中,通常会定义一个抽象的解释器类,该类包含一个解析表达式的方法和一个计算表达式值的方法。具体的解释器类则实现了抽象解释器类中的解析和计算方法,用于处理不同类型的表达式。

使用解释器模式的好处是可以将表达式的解析和计算分离开来,使得表达式的语法和语义可以独立修改。同时,由于表达式被转化为了树形结构,因此可以很容易地实现一些复杂的表达式操作,如递归、循环等。

然而,解释器模式也有一些缺点。首先,由于需要将表达式转化为树形结构,因此可能会增加系统的复杂度和性能开销。其次,如果表达式的结构过于复杂或嵌套层次过深,可能会导致解释器的设计和实现变得困难。

1.2 简单实现解释器模式

首先,定义一个表达式接口:

public interface Expression {int interpret();
}

然后,实现两个具体的表达式类:加法和乘法:

public class Addition implements Expression {private Expression left;private Expression right;public Addition(Expression left, Expression right) {this.left = left;this.right = right;}@Overridepublic int interpret() {return left.interpret() + right.interpret();}
}public class Multiplication implements Expression {private Expression left;private Expression right;public Multiplication(Expression left, Expression right) {this.left = left;this.right = right;}@Overridepublic int interpret() {return left.interpret() * right.interpret();}
}

接下来,实现一个解析器类,用于将字符串表达式解析为抽象语法树:

import java.util.Stack;public class Parser {public static Expression parse(String expression) {Stack<Character> operators = new Stack<>();Stack<Expression> operands = new Stack<>();for (int i = 0; i < expression.length(); i++) {char c = expression.charAt(i);if (Character.isDigit(c)) {operands.push(new Number(c - '0'));} else if (c == '+' || c == '*') {while (!operators.isEmpty() && hasPrecedence(c, operators.peek())) {processOperation(operators, operands);}operators.push(c);} else if (c == '(') {operators.push(c);} else if (c == ')') {while (operators.peek() != '(') {processOperation(operators, operands);}operators.pop(); // Discard the '('}}while (!operators.isEmpty()) {processOperation(operators, operands);}return operands.pop();}private static boolean hasPrecedence(char op1, char op2) {if (op2 == '(' || op2 == ')') {return false;}if ((op1 == '*' || op1 == '/') && (op2 == '+' || op2 == '-')) {return false;}return true;}private static void processOperation(Stack<Character> operators, Stack<Expression> operands) {char operator = operators.pop();Expression right = operands.pop();Expression left = operands.pop();operands.push(new BinaryOperator(operator, left, right));}
}

最后,实现一个主类来测试解释器模式:

public class Main {public static void main(String[] args) {String expression = "3+5*(2-8)";Expression tree = Parser.parse(expression);System.out.println("The result of the expression is: " + tree.interpret());}
}

运行上述代码,将输出表达式的计算结果:The result of the expression is: -13。

1.3 使用解释器模式的注意事项

  • 1、需要将表达式转化为抽象语法树(AST),因此需要考虑表达式的语法和结构,以便正确地构建AST。

  • 2、解释器模式通常用于实现一些简单的语法,如算术表达式、逻辑表达式等。对于复杂的语法,可能需要设计更复杂的解释器类和操作符类。

  • 3、在解析表达式时,需要考虑运算符的优先级和结合性,以便正确地计算表达式的值。

  • 4、解释器模式可能会增加系统的复杂度和性能开销,因此在使用时需要权衡利弊。如果表达式的结构过于复杂或嵌套层次过深,可能会导致解释器的设计和实现变得困难。

  • 5、在使用解释器模式时,需要考虑表达式的安全性问题。如果表达式中包含用户输入的数据,需要进行适当的过滤和验证,以避免安全漏洞的出现。

二、解释器模式的用途

  • 1、编译器:编译器是一种将源代码转换为目标代码的程序。解释器模式可以用于实现编译器中的语法分析和代码生成部分。

  • 2、规则引擎:规则引擎是一种基于预定义的规则来执行决策的程序。解释器模式可以用于实现规则引擎中的规则解析和执行部分。

  • 3、正则表达式:正则表达式是一种用于处理字符串的模式匹配工具。解释器模式可以用于实现正则表达式的解析和匹配部分。

  • 4、SQL解析:SQL是一种用于操作关系数据库的语言。解释器模式可以用于实现SQL解析器中的语法分析和查询优化部分。

  • 5、符号处理引擎:符号处理引擎是一种用于处理数学公式、符号运算等问题的程序。解释器模式可以用于实现符号处理引擎中的公式解析和计算部分。

  • 6、在标记权限的场景下,解释器模式也非常有用。例如,我们可以构建一个表示权限的表达式系统,通过标记不同的接口使用不同的权限表达式,从而实现访问控制。

三、解释器模式实现方式

3.1 基于递归下降实现解释器模式

要实现一个基于递归下降的Java解释器模式,首先需要定义一个抽象语法树(AST)的数据结构,然后为每个语法规则创建一个解析方法。以下是一个简单的示例:

定义AST数据结构:

abstract class Expr {
}class Literal extends Expr {final int value;Literal(int value) {this.value = value;}
}class Add extends Expr {final Expr left;final Expr right;Add(Expr left, Expr right) {this.left = left;this.right = right;}
}

实现递归下降解析方法:

class Interpreter {private final String input;private int pos = -1;private char currentChar;Interpreter(String input) {this.input = input;advance();}private void advance() {pos++;if (pos < input.length()) {currentChar = input.charAt(pos);} else {currentChar = '\0';}}private boolean eat(int charToEat) {while (currentChar == ' ') {advance();}if (currentChar == charToEat) {advance();return true;}return false;}public Expr parse() {Expr expr = parseExpression();if (pos < input.length()) {throw new RuntimeException("Unexpected: " + (char) currentChar);}return expr;}private Expr parseExpression() {Expr x = parseTerm();for (; ; ) {if      (eat('+')) x = new Add(x, parseTerm()); // additionelse if (eat('-')) x = new Subtract(x, parseTerm()); // subtractionelse return x;}}private Expr parseTerm() {Expr x = parseFactor();for (; ; ) {if      (eat('*')) x = new Multiply(x, parseFactor()); // multiplicationelse if (eat('/')) x = new Divide(x, parseFactor()); // divisionelse return x;}}private Expr parseFactor() {if (eat('+')) return parseFactor(); // unary plusif (eat('-')) return new UnaryMinus(parseFactor()); // unary minusint startPos = this.pos;while (Character.isDigit(currentChar)) advance();if (!eat(')')) {return new Literal(Integer.parseInt(input.substring(startPos, this.pos)));}return null;}
}

使用解释器解析表达式:

public static void main(String[] args) {String input = "3+5*2";Interpreter interpreter = new Interpreter(input);Expr result = interpreter.parse();System.out.println(result);
}

这个示例实现了一个简单的算术表达式解释器,支持加法、减法、乘法和除法。你可以根据需要扩展这个解释器以支持更复杂的语法规则。

3.2 基于LL(1)文法实现解释器模式

要实现基于LL(1)文法的Java解释器模式,首先需要了解LL(1)文法的概念。LL(1)文法是一种自下而上的预测分析表构造方法,它通过消除左递归和公共前缀来简化文法。接下来,我们将分步骤实现一个简单的Java解释器模式。

定义文法规则:

public class Grammar {public static final Map<String, List<String>> rules = new HashMap<>();static {rules.put("E", Arrays.asList("E+T", "T"));rules.put("T", Arrays.asList("T*F", "F"));rules.put("F", Arrays.asList("(E)", "id"));}
}

构建预测分析表:

import java.util.*;public class PredictiveParser {private static final Set<String> nonTerminals = new HashSet<>(Grammar.rules.keySet());private static final Set<String> terminals = new HashSet<>();static {for (String rule : Grammar.rules.values()) {for (String symbol : rule) {if (!nonTerminals.contains(symbol)) {terminals.add(symbol);}}}}private final Map<String, Map<String, String>> table = new HashMap<>();public PredictiveParser() {buildTable();}private void buildTable() {for (String nonTerminal : nonTerminals) {table.put(nonTerminal, new HashMap<>());for (String terminal : terminals) {table.get(nonTerminal).put(terminal, null);}}boolean changed;do {changed = false;for (String nonTerminal : nonTerminals) {for (Map.Entry<String, String> entry : table.get(nonTerminal).entrySet()) {String key = entry.getKey();for (String rule : Grammar.rules.get(nonTerminal)) {if (rule.contains(key)) {String value = rule.substring(0, rule.indexOf(key));if (table.get(nonTerminal).get(key) == null || table.get(nonTerminal).get(key).compareTo(value) > 0) {table.get(nonTerminal).put(key, value);changed = true;}}}}}} while (changed);}public String parse(String input) {int index = 0;return parse(input, index);}private String parse(String input, int index) {String currentSymbol = input.substring(index, index + 1);if (terminals.contains(currentSymbol)) {return currentSymbol;} else {String production = table.get(currentSymbol).get(currentSymbol);return production + parse(input, index + production.length());}}
}

使用解释器解析输入:

public class Main {public static void main(String[] args) {PredictiveParser parser = new PredictiveParser();String input = "id+id*id";String output = parser.parse(input);System.out.println("Input: " + input);System.out.println("Output: " + output);}
}

这个例子中,我们实现了一个简单的算术表达式解释器,它可以解析包含加法、减法、乘法和括号的表达式。注意,这个实现仅适用于给定的文法规则,实际应用中可能需要根据具体需求进行调整。

3.3 基于ANTLR的工具实现解释器模式

ANTLR是一个强大的解析器生成器,可以用于构建多种编程语言的解析器。要使用ANTLR实现解释器模式,首先需要安装ANTLR并下载相应的语法文件。然后,可以使用ANTLR工具生成Java代码,最后编写Java代码实现解释器模式。

以下是一个简单的示例:

安装ANTLR:访问ANTLR官网(https://www.antlr.org/)下载并安装ANTLR。

下载语法文件:访问ANTLR官方GitHub仓库(https://github.com/antlr/grammars-v4),选择合适的语法文件(例如,Java8.g4)。

使用ANTLR工具生成Java代码:在命令行中,切换到语法文件所在的目录,运行以下命令:

java -jar antlr-4.x-complete.jar Java8.g4

这将生成一个名为Java8BaseListener.java和Java8BaseVisitor.java的文件。

编写Java代码实现解释器模式:创建一个名为Interpreter.java的文件,内容如下:

import org.antlr.v4.runtime.*;
import org.antlr.v4.runtime.tree.*;public class Interpreter {public static void main(String[] args) throws Exception {// 读取输入文件CharStream input = CharStreams.fromFileName("input.java");// 创建词法分析器Java8Lexer lexer = new Java8Lexer(input);// 创建令牌流CommonTokenStream tokens = new CommonTokenStream(lexer);// 创建语法分析器Java8Parser parser = new Java8Parser(tokens);// 解析输入文件ParseTree tree = parser.compilationUnit();// 创建监听器Java8BaseListener listener = new Java8BaseListener() {@Overridepublic void enterEveryRule(ParserRuleContext ctx) {System.out.println("Entering rule: " + ctx.getText());}@Overridepublic void exitEveryRule(ParserRuleContext ctx) {System.out.println("Exiting rule: " + ctx.getText());}};// 遍历抽象语法树walker.walk(listener, tree);}
}

编译并运行解释器:在命令行中,切换到Interpreter.java所在的目录,运行以下命令:

javac -cp antlr-4.x-complete.jar Interpreter.java
java -cp antlr-4.x-complete.jar:. Interpreter

这将输出输入文件中每个规则的进入和退出信息。你可以根据需要修改Java8BaseListener类中的enterEveryRule和exitEveryRule方法来实现自己的解释器逻辑。

3.4 基于Lambda表达式实现解释器模式

首先,我们定义一个接口Expr,它表示一个表达式:

interface Expr {int eval();
}

然后,我们可以实现一些具体的表达式类,例如加法和乘法:

class Add implements Expr {private final Expr left;private final Expr right;public Add(Expr left, Expr right) {this.left = left;this.right = right;}@Overridepublic int eval() {return left.eval() + right.eval();}
}class Mul implements Expr {private final Expr left;private final Expr right;public Mul(Expr left, Expr right) {this.left = left;this.right = right;}@Overridepublic int eval() {return left.eval() * right.eval();}
}

接下来,我们可以使用Lambda表达式来创建这些表达式对象:

Expr add = (Expr left, Expr right) -> new Add(left, right);
Expr mul = (Expr left, Expr right) -> new Mul(left, right);

最后,我们可以使用这些表达式对象来计算表达式的值:

Expr expr1 = mul.apply(add.apply(5, 3), add.apply(2, 4));
System.out.println(expr1.eval()); // 输出:26

在这个示例中,我们使用了Lambda表达式来简化了表达式对象的创建过程。这使得代码更加简洁和易读

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/137796.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Java编写简易rabbitmq生产者与消费者

一、前言 开发时经常与其它系统用rabbitmq对接&#xff0c;当需要自测时&#xff0c;还是自己写rabbitmq生产者、消费者自测方便些。 下面总结下不用框架、使用java编写简易rabbitmq的方法。 二、代码 1.导入jar包 (1)如果是maven&#xff0c;那就用 <dependency>&…

百度智能云正式上线Python SDK版本并全面开源!

文章目录 1. SDK的优势2. 千帆SDK&#xff1a;快速落地LLM应用3. 如何快速上手千帆SDK3.1 SDK快速启动3.2 SDK进阶指引3.3 通过Langchain接入千帆SDK 4. 开源社区 百度智能云千帆大模型平台再次升级&#xff01;在原有API基础上&#xff0c;百度智能云正式上线Python SDK&#…

直播会议一体机安卓主板_5G智能会议一体机双屏异显设计

5G直播会议一体机主板是专门为支持音视频输入输出而设计的&#xff0c;内置有安卓13系统&#xff0c;可兼容多种直播和会议软件。该产品可广泛应用于智能会议一体机、便携式直播设备、录播导播、无人机直播以及视频传输等多个领域。 这款主板采用了国产6纳米旗舰芯片紫光展锐T8…

虚幻C++基础 day4

虚幻中的UI 虚幻中的比较常用的UI&#xff1a;Widget Blueprint又称UMG虚幻中的两种布局&#xff1a; 网格布局锚布局 创建Widget Blueprint 网格布局 有点类似Qt中的网格布局&#xff0c;将UI面板进行行列切分Horizontal Box&#xff1a;水平分布Vertical Box&#xff1a;…

机器学习---多分类SVM、支持向量机分类

1. 多分类SVM 1.1 基本思想 Grammer-singer多分类支持向量机的出发点是直接用超平面把样本空间划分成M个区域&#xff0c;其 中每个区域对应一个类别的输入。如下例&#xff0c;用从原点出发的M条射线把平面分成M个区域&#xff0c;下图画 出了M3的情形&#xff1a; 1.2 问题…

【Spring之底层核心架构概念解析】

文章目录 一、BeanDefinition二、BeanDefinitionReader2.1、AnnotatedBeanDefinitionReader2.2、XmlBeanDefinitionReader 五、ClassPathBeanDefinitionScanner六、BeanFactory七、ApplicationContext7.1、AnnotationConfigApplicationContext7.2、ClassPathXmlApplicationCont…

2023年11月PHP测试覆盖率解决方案

【题记&#xff1a;最近进行了ExcelBDD PHP版的开发&#xff0c;查阅了大量资料&#xff0c;发现PHP测试覆盖率解决方案存在不同的历史版本&#xff0c;让我花费了蛮多时间&#xff0c;为了避免后人浪费时间&#xff0c;整理本文&#xff0c;而且网上没有给出Azure DevOps里面P…

jQuery HTML/CSS 参考文档

jQuery HTML/CSS 参考文档 文章目录 应用样式 示例属性方法示例 jQuery HTML/CSS 参考文档 应用样式 addClass( classes ) 方法可用于将定义好的样式表应用于所有匹配的元素上。可以通过空格分隔指定多个类。 示例 以下是一个简单示例&#xff0c;设置了para标签 <p&g…

超详细!Linux内核内存规整详解

1.前言 伙伴系统作为内核最基础的物理页内存分配器&#xff0c;具有高效、实现逻辑简介等优点&#xff0c;其原理页也尽可能降低内存外部碎片产生&#xff0c;但依然无法杜绝碎片问题。外部碎片带来的最大影响就是内存足够&#xff0c;但是却无法满足内存分配需求&#xff0c;如…

docker创建并访问本地前端

docker创建并访问本地前端&#xff0c;直接上命令&#xff1a; 安装nginx镜像&#xff1a; docker pull nginx 查看已安装的nginx&#xff1a; docker images 创建DockerFile文件&#xff0c;直接在当前文件夹种创建 touch Dockerfile 在Dockerfile写入内容&#xff1a; F…

【仿真动画】人机协作机器人自动化产线仿真动画欣赏

人机协作机器人自动化产线仿真动画 动画部分思维导图 机器人自动化产线仿真动画是利用三维动画技术对机器人自动化产线进行仿真模拟&#xff0c;以直观、形象的方式展示产线的运行情况。它具有以下作用&#xff1a; 提高沟通效率 机器人自动化产线的设计、实施和维护涉及多个部…

JSON方法实现深拷贝存在的问题

现在的前端面试中&#xff0c;深拷贝出现的频率极高。常规的问题中&#xff0c;可能首先问你&#xff0c;什么是深拷贝&#xff0c;实现深拷贝的方式都有哪些&#xff0c;你可能会答出几点&#xff0c;比如通过JSON对象提供的JSON.strinfy和JSON.parse来实现&#xff0c;因为这…

【算法】算法题-20231111

一、409. 最长回文串 简单 给定一个包含大写字母和小写字母的字符串 s &#xff0c;返回 通过这些字母构造成的 最长的回文串 。 在构造过程中&#xff0c;请注意 区分大小写 。比如 “Aa” 不能当做一个回文字符串。 示例 1: 输入:s “abccccdd” 输出:7 解释: 我们可以构…

Kotlin库实现多线程爬取数据

由于字数限制&#xff0c;以下是一个简化版的爬虫程序示例&#xff0c;使用了Kotlin的网络库kotlinx.coroutines和kotlinx.html。这个程序会爬取一个简单的Python多线程跑数据的网页&#xff0c;并打印出结果。 import kotlinx.coroutines.* import kotlinx.html.* import java…

CSS3 用户界面、图片、按钮

一、CSS3用户界面&#xff1a; 在CSS3中&#xff0c;增加了一些新的用户界面特性来调整元素尺寸、框尺寸和外边框。CSS3用户界面属性&#xff1a;resize、box-sizing、outline-offset。 1、resize&#xff1a; resize属性指定一个元素是否应该由用户去调整大小。 <style…

amazon产品采集数据

导入需要的库&#xff1a;requests&#xff0c;BeautifulSoup&#xff0c;re&#xff0c;chardet requests用于发送HTTP请求&#xff1b;BeautifulSoup用于解析HTML&#xff1b;re用于正则表达式&#xff1b;chardet用于识别网页编码。 定义函数&#xff0c;接受URL参数&#…

美格智能5G RedCap模组顺利完成中国联通5G物联网OPENLAB开放实验室认证

近日&#xff0c;美格智能5G RedCap模组SRM813Q顺利通过中国联通5G物联网OPENLAB开放实验室端到端的测试验收&#xff0c;并获得OPENLAB实验室的认证证书。这标志着该模组产品各项性能均已符合RedCap商用标准&#xff0c;为5G RedCap规模商用奠定了坚实基础。 中国联通5G物联网…

MySQL查询语句练习题,测试基本够用了

1.创建student和score表 CREATE TABLE student ( id INT(10) NOT NULL UNIQUE PRIMARY KEY , name VARCHAR(20) NOT NULL , sex VARCHAR(4) , birth YEAR, department VARCHAR(20) , address VARCHAR(50) ); 创建score表。SQL代码如下&#xff1a; CREATE TA…

C#学习系列之事件

C#学习系列之事件 前言事件发布者和订阅者事件触发和注册事件声明事件订阅事件触发使用 总结 前言 基础学习。 事件 发布者和订阅者 发布者&#xff1a;通知某件事情发生的。 订阅者&#xff1a;对某件事情关注的。 事件触发和注册 触发&#xff1a;事件发生就通知所有关…

8255 boot介绍及bring up经验分享

这篇文章会简单的介绍8255的启动流程&#xff0c;然后着重介绍8255在实际项目中新硬件上的bring up工作&#xff0c;可以给大家做些参考。 8255 boot介绍 下面这些信息来自文档&#xff1a;《QAM8255P IVI Boot and CoreBSP Architecture Technical Overview》 80-42847-11 R…