如何确定1000个潜在关键字中的给定关键字?

时间:2014-08-28 03:26:47

标签: c++ performance parsing

所以我不是专业的开发人员,但我定期编程。我正在寻找编写代码,并寻找一些关于管理解析器的建议,该解析器正在读取文本文件,将每行视为字符串,并尝试确定该行上的输入。任何给定的行可能是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
            }

3 个答案:

答案 0 :(得分:5)

  

一旦我匹配给定的关键字,我计划跳转到一个例程,该例程实例化该关键字类型的对象。

您不想拥有1,000种不同的IF语句,这是正确的。

抽象地说,我建议考虑一下老式的卡片目录是如何工作的(假设你曾见过一个,年轻人还知道这些是什么吗?)

enter image description here

卡片目录很有用,因为您不是从第一个抽屉开始并按顺序查看所有项目,然后转到下一个抽屉。相反,您可以使用快速测试来了解要查看的抽屉。快速测试涉及指纹或候选人"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)。

std::unordered_map

在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)

两种解决方案:

  1. 使用查找表。如果您的关键字列表完全是动态的,那么这样做会更好;如果您使用哈希表,它可以是 O(1)

  2. 使用词法分析器,例如 flex(1),并将关键字构建到.l文件中。这可以更快,因为它一次进行一个字符而没有最终查找步骤,但它只适用于事先完全修复关键字列表,例如用编程语言。