所以我不是专业的开发人员,但我定期编程。我正在寻找编写代码,并寻找一些关于管理解析器的建议,该解析器正在读取文本文件,将每行视为字符串,并尝试确定该行上的输入。任何给定的行可能是1000多个不同的关键字之一,这是困难的部分。一旦我有了这个关键字,我觉得必须有一个更有效的方法来确定它是什么,而不是实现1000 if-else语句或1000 case-break语句。一旦我匹配给定的关键字,我计划跳转到一个例程,该例程实例化该关键字类型的对象。在找到我的目标之前,我不想进行999次测试,这只是我觉得的浪费。我尝试按字母顺序将其分解,这大大减少了它,但仍然存在大量无法管理的if-else语句。
我已经发现我不能嵌套超过128个if-else语句,所以我目前的替代方案是1000s of just" if"没有匹配的陈述"否则"陈述,我知道这是一种不好的做法。所以这是我当前代码的概括:
if (keyword_str.compare(keyword1)) {
Parse(keyword1); // A general routine to parse all these similarly formatted keywords
}
if (keyword_str.compare(keyword2)) {
Parse(keyword2);
}
if (keyword_str.compare(keyword3)) {
Parse(keyword3);
}
//
//
//
if (keyword_str.compare(keyword999)) {
Parse(keyword999);
}
if (keyword_str.compare(keyword1000)) {
Parse(keyword1000);
}
任何帮助将不胜感激!谢谢!
好的,所以这就是我所关注的问题,但仍然有点迷失在如何使用地图来确定对象类型,然后实例化该对象。以下是一些代码段:
class baseClass
{
public:
baseClass();
~baseClass();
};
//
// keyword1 class declaration
class keyword1 : public baseClass
{
public:
// Constructors
keyword1 () { cout << "keyword1 constructor..." << endl;}
~keyword1 () { cout << "keyword1 destructor..." << endl;}
protected:
};
//
// keyword2 class declaration
class keyword2 : public baseClass
{
public:
// Constructors
keyword2 () { cout << "keyword2 constructor..." << endl;}
~keyword2 () { cout << "keyword2 destructor..." << endl;}
protected:
};
//
// keyword3 class declaration
class keyword3 : public baseClass
{
public:
// Constructors
keyword3 () { cout << "keyword3 constructor..." << endl;}
~keyword3 () { cout << "keyword3 destructor..." << endl;}
protected:
};
//
//*******************
map <string, baseClass> keyword_map;
keyword_map.insert (make_pair ("keyword1", keyword1 )); // ########## This is where I'm lost
keyword_map.insert (make_pair ("keyword2", keyword2 )); // ########## This is where I'm lost
keyword_map.insert (make_pair ("keyword3", keyword3 )); // ########## This is where I'm lost
// Search for keyword
string searching_for = "keyword3";
map <string, baseClass> ::const_iterator it = keyword_map.find(searching_for);
if (it == keyword_map.end()) {
cout << "No keyword found." << endl;
}
else
{
cout << "Found the keyword!" << endl;
it->second; // ########## This is where I'm lost
}
答案 0 :(得分:5)
一旦我匹配给定的关键字,我计划跳转到一个例程,该例程实例化该关键字类型的对象。
您不想拥有1,000种不同的IF语句,这是正确的。
抽象地说,我建议考虑一下老式的卡片目录是如何工作的(假设你曾见过一个,年轻人还知道这些是什么吗?)
卡片目录很有用,因为您不是从第一个抽屉开始并按顺序查看所有项目,然后转到下一个抽屉。相反,您可以使用快速测试来了解要查看的抽屉。快速测试涉及指纹或候选人"hash"。旧图书馆卡目录通常使用非常简单的“哈希函数”(前一个或两个字母;“此抽屉包含标题以”S-Ti“开头的书籍的所有卡片。)您缩小了所需的比较数量基于该测试做的只能看一个抽屉。
如果要想出一种指纹字符串的方法,并将它们归档到像这样的桶中,那么你很幸运。这是在标准库的引擎下已经完成的所有工作。除非您的需求非常专业(或者您的关键字中包含奇怪的模式,而且它们都具有相同的“指纹”)... std::unordered_map应该有效。
选择代表关键字的std::string
“关键字”。 “价值”将是某种工厂......从关键字后面的东西创建对象的方法。这可能是你“更好的方式”的基础......
..但
在这种情况下初始化std::unordered_map
以进行出价时,如果地图中的“值”分别适合构建不同的类,则1000就是大量的类。在输入class ObjectOne
并将其编号为class ObjectOneThousand
之前,您可能希望列出更多细节,这听起来像执行1000个IF语句进行比较一样有问题。
所以也许你应该在聊天或其他论坛中寻求更多的评论,然后再继续这个想法。
更新以响应修改
您的代码在关键字类别方面存在问题。它们是否打算代表关键字类(因为......只有那么多的实例化,因为你有关键字?)一个人应该怀疑只有一个实例和代表一类东西;这就是班级本身的意义所在。如果这是有道理的。 : - /
因此,您想要放在地图中的不是关键字的实例。更重要的是,您在概念上想要放置稍后调用的关键字类。在精神上,这将是:
#include <typeinfo>
map <string, type_info &> keyword_map;
keyword_map.insert (make_pair ("keyword1", typeid(keyword1) ));
keyword_map.insert (make_pair ("keyword2", typeid(keyword2) ));
keyword_map.insert (make_pair ("keyword3", typeid(keyword3) ));
您可能会认为以后可以使用type_info调用某种make_class
,但它不会那样工作。因此......存储工厂函数以获得该行为的想法。我将用静态成员函数给你一个简单的答案,所以在每个关键字类中你都有这样的东西:
class keyword1 : public baseClass {
// ...
static shared_ptr<baseClass> factory() {
return make_shared<keyword3>();
}
// ...
};
因为它是一个静态成员,它就像一个普通的函数。你可以获取它的地址,存储指针,然后在没有任何类实例的情况下调用它来调用它。它返回一个指向基类的共享指针,尽管你最终会得到的是一个指向基类的指针...它会对你在基类的接口中定义的任何虚函数进行多态化处理,适合于那种类型关键字是。
(请注意,在这种情况下您需要make your destructors virtual!默认情况下最好这样做,如果您有充分的理由,则不要这样做。)
map <string, shared_ptr<baseClass>(*)()>> keyword_map;
keyword_map.insert (make_pair ("keyword1", &keyword1::factory ));
keyword_map.insert (make_pair ("keyword2", &keyword2::factory ));
keyword_map.insert (make_pair ("keyword3", &keyword3::factory ));
现在稍后当您找到关键字时,可以调用从find
返回的函数来获取相应关键字类的实例。然后做你打算用对象实例做的任何事情。
但是我想你会发现很难在基类上定义一个满足你这种设计的接口。这就是为什么我说1000个班级表明你可能没有问题,你想要接近你的想法。我还想象你会有很多其他问题,但请把它们作为自己的新问题帖子。 : - )
答案 1 :(得分:2)
unordered_map
在这里可以很快地运作。它被实现为哈希映射,因此它的查找性能大致为O(1)。
在C ++ 11中,您可以使用std::string
作为密钥,使用std::function<>
作为您的值。我在这里写了一个例子,展示了如何在unordered_map
中使用lambda函数:
#include <iostream>
#include <unordered_map>
#include <functional>
using namespace std;
typedef unordered_map<string, function<void ()>> ufmap;
ufmap M;
int main() {
// Create Keys and functions
string s_a = "A";
function<void ()> f_a = [](){
cout << "Construct type A here" << endl;
};
string s_b = "B";
function<void ()> f_b = [](){
cout << "Construct type B here" << endl;
};
// Add Keys and functions to the map
M.insert(pair<string, function<void ()>>(s_a, f_a));
M.insert(pair<string, function<void ()>>(s_b, f_b));
// Finding a string and using its function
string searching_for = "A";
ufmap::const_iterator it = M.find(searching_for);
if (it == M.end()) {
// String key wasn't found
}
else {
it->second();
}
}
答案 2 :(得分:1)
两种解决方案:
使用查找表。如果您的关键字列表完全是动态的,那么这样做会更好;如果您使用哈希表,它可以是 O(1)。
使用词法分析器,例如 flex(1),并将关键字构建到.l文件中。这可以更快,因为它一次进行一个字符而没有最终查找步骤,但它只适用于事先完全修复关键字列表,例如用编程语言。