《设计模式的艺术》笔记 - 解释器模式

介绍

解释器模式定义一个语言的文法,并且建立一个解释器来解释该语言中的句子,这里的"语言"是指使用规定格式和语法的代码。解释器模式是一种类行为型模式。

实现

myclass.h

cpp 复制代码
//
// Created by yuwp on 2024/1/12.
//

#ifndef DESIGNPATTERNS_MYCLASS_H
#define DESIGNPATTERNS_MYCLASS_H

#include <iostream>
#include <unordered_map>
#include <atomic>
#include <vector>
#include <memory>

class Context {

};

class AbstractExpression { // 抽象表达式
public:
    virtual float interpret() = 0;
};

class Add : public AbstractExpression {    // 非终结符表达式
public:
    Add(std::shared_ptr<AbstractExpression> left, std::shared_ptr<AbstractExpression> right);

    float interpret() override;

private:
    std::shared_ptr<AbstractExpression> m_left;
    std::shared_ptr<AbstractExpression> m_right;
};

class Substract : public AbstractExpression {    // 非终结符表达式
public:
    Substract(std::shared_ptr<AbstractExpression> left, std::shared_ptr<AbstractExpression> right);

    float interpret() override;

private:
    std::shared_ptr<AbstractExpression> m_left;
    std::shared_ptr<AbstractExpression> m_right;
};

class Multiply : public AbstractExpression {    // 非终结符表达式
public:
    Multiply(std::shared_ptr<AbstractExpression> left, std::shared_ptr<AbstractExpression> right);

    float interpret() override;

private:
    std::shared_ptr<AbstractExpression> m_left;
    std::shared_ptr<AbstractExpression> m_right;
};

class Division : public AbstractExpression {    // 非终结符表达式
public:
    Division(std::shared_ptr<AbstractExpression> left, std::shared_ptr<AbstractExpression> right);

    float interpret() override;

private:
    std::shared_ptr<AbstractExpression> m_left;
    std::shared_ptr<AbstractExpression> m_right;
};

class Negative : public AbstractExpression {    // 非终结符表达式
public:
    Negative(std::shared_ptr<AbstractExpression> exp);

    float interpret() override;

private:
    std::shared_ptr<AbstractExpression> m_exp;
};

class Value : public AbstractExpression {
public:
    Value(float val);

    float interpret() override;

private:
    float m_val;
};

class ExpressionParser {
public:
    ExpressionParser(const std::string &expStr);

    std::shared_ptr<AbstractExpression> getExpression();

private:
    void popOperation();

    std::vector<char> m_ops;
    std::vector<std::shared_ptr<AbstractExpression>> m_vals;
};

#endif //DESIGNPATTERNS_MYCLASS_H

myclass.cpp

cpp 复制代码
//
// Created by yuwp on 2024/1/12.
//

#include "myclass.h"
#include <thread>
#include <unistd.h>

Add::Add(std::shared_ptr<AbstractExpression> left, std::shared_ptr<AbstractExpression> right) {
    m_left = left;
    m_right = right;
}

float Add::interpret() {
    return m_left->interpret() + m_right->interpret();
}

Substract::Substract(std::shared_ptr<AbstractExpression> left, std::shared_ptr<AbstractExpression> right) {
    m_left = left;
    m_right = right;
}

float Substract::interpret() {
    return m_left->interpret() - m_right->interpret();
}

Multiply::Multiply(std::shared_ptr<AbstractExpression> left, std::shared_ptr<AbstractExpression> right) {
    m_left = left;
    m_right = right;
}

float Multiply::interpret() {
    return m_left->interpret() * m_right->interpret();
}

Division::Division(std::shared_ptr<AbstractExpression> left, std::shared_ptr<AbstractExpression> right) {
    m_left = left;
    m_right = right;
}

float Division::interpret() {
    return m_left->interpret() / m_right->interpret();
}

Negative::Negative(std::shared_ptr<AbstractExpression> exp) {
    m_exp = exp;
}

float Negative::interpret() {
    return -m_exp->interpret();
}

Value::Value(float val) {
    m_val = val;
}

float Value::interpret() {
    return m_val;
}

ExpressionParser::ExpressionParser(const std::string &expStr) {
    char prev = '\0', c, op;
    std::shared_ptr<AbstractExpression> exp;
    std::unordered_map<char, char> brackets = {{'}', '{'}, {']', '['}, {')', '('}};
    float val;

    for (int i = 0; i < expStr.length(); ++i) {
        c = expStr[i];
        if (c == ' ') {
            continue;
        } else if (c == '+') {
            while (m_ops.size() > 0 && m_ops[m_ops.size() - 1] != '{' && m_ops[m_ops.size() - 1] != '[' && m_ops[m_ops.size() - 1] != '(') {
                popOperation();
            }
            m_ops.push_back(c);
            prev = c;
        } else if (c == '-') {
            if (prev == '\0' || prev == '{' || prev == '[' || prev == '(') {
                m_vals.push_back(0);
            } else {
                while (m_ops.size() > 0 && m_ops[m_ops.size() - 1] != '{' && m_ops[m_ops.size() - 1] != '[' && m_ops[m_ops.size() - 1] != '(') {
                    popOperation();
                }
            }
            m_ops.push_back(c);
            prev = c;
        } else if (c == '*' || c == '/') {
            if (m_ops.size() > 0 && (m_ops[m_ops.size() - 1] == '*' || m_ops[m_ops.size() - 1] == '/')) {
                popOperation();
            }
            m_ops.push_back(c);
            prev = c;
        } else if (c == '{' || c == '[' || c == '(') {
            m_ops.push_back(c);
            prev = c;
        } else if (c == '}' || c == ']' || c == ')') {
            while (m_ops.size() > 0) {
                op = m_ops[m_ops.size() - 1];
                if (op == brackets[c]) {
                    m_ops.pop_back();
                    break;
                } else {
                    popOperation();
                }
            }
        } else if (c >= '0' && c <= '9'){
            char buf[128];
            int j = 0;
            while (c == '.' || (c >= '0' && c <= '9')) {
                buf[j] = c;
                j++;
                i++;
                c = expStr[i];
            }
            prev = '0';
            --i;
            buf[j] = '\0';
            val = atof(buf);
            exp.reset(new Value(val));
            m_vals.push_back(exp);
        }
    }

    if (m_ops.size() > 0) {
        popOperation();
    }
    if (m_vals.size() != 1) {
        goto failed;
    }
    return;

failed:
    m_vals.resize(1);
    m_vals[0] = nullptr;
}

void ExpressionParser::popOperation() {
    char op = m_ops[m_ops.size() - 1];
    std::shared_ptr<AbstractExpression> exp;

    if (op == '+') {
        exp.reset(new Add(m_vals[m_vals.size() - 2], m_vals[m_vals.size() - 1]));
    } else if (op == '-') {
        exp.reset(new Substract(m_vals[m_vals.size() - 2], m_vals[m_vals.size() - 1]));
    } else if (op == '*') {
        exp.reset(new Multiply(m_vals[m_vals.size() - 2], m_vals[m_vals.size() - 1]));
    } else if (op == '/') {
        exp.reset(new Division(m_vals[m_vals.size() - 2], m_vals[m_vals.size() - 1]));
    } else {
        return;
    }
    m_ops.pop_back();
    m_vals.pop_back();
    m_vals.pop_back();
    m_vals.push_back(exp);
}

std::shared_ptr<AbstractExpression> ExpressionParser::getExpression() {
    return m_vals[0];
}

main.cpp

cpp 复制代码
#include <iostream>
#include <mutex>
#include "myclass.h"

int main() {
    ExpressionParser *expressionParser = new ExpressionParser("(1+2)*(6-2)/2+1");
    auto exp = expressionParser->getExpression();
    if (exp) {
        std::cout << exp->interpret() << std::endl;
    } else {
        std::cout << 0 << std::endl;
    }

    return 0;
}

总结

优点

  1. 易于改变和扩展文法。由于在解释器模式中使用类来表示语言的文法规则,因此可以通过继承等机制来改变或扩展文法。

  2. 每一条文法规则都可以表示为一个类,因此可以方便地实现一个简单的语言。

  3. 实现文法较为容易。在抽象语法树中每一个表达式节点类的实现方式都是相似的,这些类的代码编写都不会特别复杂,还可以通过一些工具自动生成节点类代码。

  4. 增加新的解释表达式较为方便。如果用户需要增加新的解释表达式只需要对应增加一个新的终结符表达式或非终结符表达式类,原有表达式类代码无须修改,符合开闭原则。

缺点

  1. 对于复杂文法难以维护。在解释器模式中,每一条规则至少需要定义一个类,因此如果一种语言包含太多文法规则,类的个数将会急剧增加,导致系统难以管理和维护,此时可以考虑使用语法分析程序等方式来取代解释器模式。

  2. 执行效率较低。由于在解释器模式中使用了大量的循环和递归调用,因此在解释较为复杂的句子时其速度很慢,而且代码的调试过程也比较麻烦。

适用场景

  1. 可以将一个需要解释执行的语言中的句子表示为一个抽象语法树。

  2. 一些重复出现的问题可以用一种简单的语言来进行表达。

  3. 一个语言的文法较为简单。

  4. 执行效率不是关键问题。(注:高效的解释器通常不是通过直接解释抽象语法树来实现的,而是需要将它们转换成其他形式,使用解释器模式的执行效率并不高。)

练习

myclass.h

cpp 复制代码
//
// Created by yuwp on 2024/1/12.
//

#ifndef DESIGNPATTERNS_MYCLASS_H
#define DESIGNPATTERNS_MYCLASS_H

#include <iostream>
#include <unordered_map>
#include <atomic>
#include <vector>
#include <memory>

/**
 * COPY VIEW FROM srcDB TO desDB 表示将数据库srcDB中的所有视图对象都复制至数据库desDB
 * MOVE TABLE Student FROM srcDB TO desDB 表示将数据库srcDB中的Student表移动至数据库desDB
 * expression ::= operation object FROM string to string
 * operation ::= 'COPY' | 'MOVE'
 * object ::= 'VIEW' | 'TABLE string'
 */

class AbstractNode {
public:
    virtual std::string interpret() = 0;
};

class SentenceNode : public AbstractNode {  // expression 简单句子,非终结符表达式
public:
    SentenceNode(std::shared_ptr<AbstractNode> operation, std::shared_ptr<AbstractNode> object, std::string &srcDB, std::string &desDB);

    std::string interpret() override;

private:
    std::shared_ptr<AbstractNode> m_operation;
    std::shared_ptr<AbstractNode> m_object;
    std::string m_srcDB;
    std::string m_desDB;
};

class OperationNode : public AbstractNode { // 终结符表达式
public:
    OperationNode(std::string operation);
    std::string interpret() override;
private:
    std::string m_operation;
};

class ObjectNode : public AbstractNode {    // 终结符表达式
public:
    ObjectNode(std::string object, std::string name = "");

    std::string interpret() override;

private:
    std::string m_object;
    std::string m_name;
};

class InstructionHandler {
public:
    void handle(std::string &instruction);

    std::string output();

private:
    std::shared_ptr<AbstractNode> m_node;
};

#endif //DESIGNPATTERNS_MYCLASS_H

myclass.cpp

cpp 复制代码
//
// Created by yuwp on 2024/1/12.
//

#include "myclass.h"
#include <thread>
#include <unistd.h>
#include <sstream>


SentenceNode::SentenceNode(std::shared_ptr<AbstractNode> operation, std::shared_ptr<AbstractNode> object,
                           std::string &srcDB, std::string &desDB) {
    m_operation = operation;
    m_object = object;
    m_srcDB = srcDB;
    m_desDB = desDB;
}

std::string SentenceNode::interpret() {
    return "将" + m_srcDB + "的" + m_object->interpret() + m_operation->interpret() + "到" + m_desDB;
}

OperationNode::OperationNode(std::string operation) {
    m_operation = operation;
}

std::string OperationNode::interpret() {
    if (m_operation == "COPY") {
        return "拷贝";
    } else if (m_operation == "MOVE") {
        return "移动";
    } else {
        return "无效指令";
    }
}

ObjectNode::ObjectNode(std::string object, std::string name) {
    m_object = object;
    m_name = name;
}

std::string ObjectNode::interpret() {
    if (m_object == "VIEW") {
        return "视图";
    } else if (m_object == "TABLE") {
        return m_name + "表";
    } else {
        return "无效指令";
    }
}

void InstructionHandler::handle(std::string &instruction) {
    std::istringstream iss(instruction);
    std::string token, token2, srcDB, desDB;
    std::shared_ptr<AbstractNode> op, obj;
    int i = 0;
    while (iss >> token) {
        if (token == "TO") {
            continue;
        } else if (token == "FROM") {
            if (i == 1) {
                obj.reset(new ObjectNode(token2, ""));
                ++i;
            }
            continue;
        }
        if (i == 0) {
            op.reset(new OperationNode(token));
            ++i;
        } else if (i == 1) {
            if (token2.length() <= 0) {
                token2 = token;
                continue;
            } else {
                obj.reset(new ObjectNode(token2, token));
                ++i;
            }
        } else if (i == 2){
            srcDB = token;
            ++i;
        } else if (i == 3) {
            desDB = token;
            ++i;
        }
    }
    m_node.reset(new SentenceNode(op, obj, srcDB, desDB));
}

std::string InstructionHandler::output() {
    return m_node->interpret();
}

main.cpp

cpp 复制代码
#include <iostream>
#include <mutex>
#include "myclass.h"

int main() {
    InstructionHandler *instructionHandler = new InstructionHandler();
    std::string ins = "COPY VIEW FROM AA_DB TO BB_DB";
    instructionHandler->handle(ins);
    std::cout << instructionHandler->output() << std::endl;

    ins = "COPY TABLE student FROM AA_DB TO BB_DB";
    instructionHandler->handle(ins);
    std::cout << instructionHandler->output() << std::endl;

    delete instructionHandler;
    return 0;
}
相关推荐
丫头,冲鸭!!!2 分钟前
B树(B-Tree)和B+树(B+ Tree)
笔记·算法
听忆.22 分钟前
手机屏幕上进行OCR识别方案
笔记
Selina K1 小时前
shell脚本知识点记录
笔记·shell
1 小时前
开源竞争-数据驱动成长-11/05-大专生的思考
人工智能·笔记·学习·算法·机器学习
霍格沃兹测试开发学社测试人社区2 小时前
软件测试学习笔记丨Flask操作数据库-数据库和表的管理
软件测试·笔记·测试开发·学习·flask
幸运超级加倍~2 小时前
软件设计师-上午题-16 算法(4-5分)
笔记·算法
王俊山IT3 小时前
C++学习笔记----10、模块、头文件及各种主题(一)---- 模块(5)
开发语言·c++·笔记·学习
Yawesh_best4 小时前
思源笔记轻松连接本地Ollama大语言模型,开启AI写作新体验!
笔记·语言模型·ai写作
CXDNW5 小时前
【网络面试篇】HTTP(2)(笔记)——http、https、http1.1、http2.0
网络·笔记·http·面试·https·http2.0
使者大牙5 小时前
【大语言模型学习笔记】第一篇:LLM大规模语言模型介绍
笔记·学习·语言模型