我正在编写一个自定义表达式评估器,以进一步了解它是如何工作的。我目前正处于代币化的第一阶段。
我之前在不同的编程语言(Python 和 C#)中多次遇到以下问题,但我不明白应该如何正确处理这个问题。
我想将存储为字符串的表达式转换为标记向量,以便稍后用于计算表达式。我不想只存储字符串向量,因为在评估过程中处理起来有点烦人。
以下是一些示例令牌:
当然,将这些标记存储在向量中意味着我稍后必须读取该标记,这就是问题的关键所在。
如何创建满足这些要求的类结构?
我首先尝试根据它们所拥有的内容创建类,并且所有类都继承自基类。
(以下代码仅显示声明。)
class Token {};
class NumberToken : public Token {
public:
int value;
NumberToken(int value);
};
class OperatorToken : public Token {
public:
OperatorType operator_type;
OperatorToken(OperatorType operator_type);
};
class BracketToken : public Token {
public:
BracketType bracket_type;
BracketToken(BracketType bracket_type);
};
我遇到的问题是在将它们全部放入向量并尝试从中读取时遇到的:
using sptr = std::shared_ptr; // Only used for readability purposes in this code snippet.
// Creating the vector is nice!
sptr<NumberToken> num = std::make_shared<NumberToken>(NumberToken(100));
sptr<OperatorToken> op = std::make_shared<OperatorToken>(OperatorToken(OperatorType::Addition));
sptr<BracketToken> br = std::make_shared<BracketToken>(BracketToken(BracketType::Open));
std::vector<sptr<Token>> tokens = {num, op, br};
// But reading from it requires dynamic_cast which from what I heard means is an indication of bad code design due to it being type switching...
for (sptr<Token> token : tokens) {
if (NumberToken* numToken = dynamic_cast<NumberToken*>(token)) {
// code for handing a number token
} else if (OperatorToken* opToken = dynamic_cast<OperatorToken*>(token)) {
// code for handing an operator token
}
...
}
我的第二次尝试涉及基类,只是将每个类的所有不同方法作为纯虚拟方法,但是..显然这是不对的。例如,NumberToken 类将具有没有任何意义的operator_type 字段。
像您一样存储为通用类型很好,但在某些时候(并非总是)您必须知道您正在操作哪种类型。
动态演员阵容在这种情况下有效。
唯一的缺点是,找到真正位于下面的类型可能会非常慢,您必须尝试所有类型。
我建议进行枚举,这样您就可以使用 switch/case 语句,即
O(1)
,而不是一系列 dynamic_cast
,即 O(n)