2014-08-28 67 views
3

所以我不是一个专业的开发人员,但我定期编程。我正在寻找编写代码,并寻找一些关于管理正在读取文本文件的解析器的建议,将每行看作一个字符串,并试图确定该行的输入。任何给定的行可以是超过1000个不同的关键字之一,这是困难的部分。一旦我有了这个关键字,我觉得必须有一个更有效的方法来确定它是什么,而不是实现1000个if-else语句或1000个case-break语句。一旦我匹配给定的关键字,我打算跳转到一个常规例程来实例化该关键字类型的对象。我不想在找到我的目标之前进行999次测试,这只是我感觉的浪费。我试着按字母顺序将其分解,这大大减少了它,但仍然有大量的if-else语句难以管理。如何确定1000个潜在关键字中的给定关键字?

我已经发现我不能嵌套超过128个if-else语句,所以我当前的选择是只有“if”语句的1000个没有匹配“else”语句,我知道这是一个不好的做法。所以这里是我现在的代码的概括:

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 
      } 
+1

你应该学习表和循环。 – stark 2014-08-28 03:28:19

+0

@stark虽然表和循环是很好的事情要知道,一个'std :: unordered_map'可以正确调度这种东西。更大的问题是*“你真的要定义1000种对象类型吗?......为什么......?”*我提出问题+1,希望知道如何做得更好,而不是仅仅去提前做出坚强的事......这是很多人会做的事情。 :-) – HostileFork 2014-08-28 03:58:30

+0

感谢大家的回复。这是我第一次使用这个文章,并且我对恢复反馈的速度印象深刻。再次感谢您的帮助。 // // // stark - 我会检查出来的。 // // // HostileFork - 我会再看看那个容器。我想我可以抓住映射的值,并将其传递给通用解析例程。尽管如此,我仍然需要遍历容器,但这会比1000个if-else/case-break语句更少混乱。谢谢! – 2014-08-28 21:20:55

回答

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语句进行比较一样有问题。

所以也许你应该在继续这个想法之前在聊天或其他论坛上寻求更多的评论。


UPDATE回应编辑

您的代码中的关键字类是什么方面的问题。他们是否打算代表关键词类别(因为在...中只有尽可能多的实例化,因为您有关键字?)应该怀疑只有一个实例并代表一类事物的类;这就是班级本身的用途。如果这是有道理的。 : -/

所以你想放在你的地图不是关键字的实例。更重要的是,您在概念上想要放入稍后调用的关键字类。在精神,这将是这样的:

#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))); 

你可能会认为,以后你可以叫某种make_class与TYPE_INFO,但它不工作的方式。因此......存储工厂函数以获得该行为的想法。我给你一个静态成员函数一个简单的答案,所以在每个关键字类,你就会有这样的事情:

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得到的函数来获得相应的关键字类的一个实例。然后不管你打算如何处理对象实例。

但我想你会发现很难定义一个接口在基类上满足你这样的设计。这又是为什么我说1,000个班级表明你可能没有问题想要按照你认为的方式进行。我也想象你会有很多其他问题,但请让他们自己创建新的问题帖子。 :-)

+0

+1的美丽图片。 – 2014-08-28 04:15:58

+0

1000可能会像一些关键字一样大,但它对于地图来说不是一个大数字。它很小。 – EJP 2014-08-28 07:08:32

+0

@EJP这将是大量的* class *类型,这可能会隐含在OP的提议中的map *值中。当您处理1,000个关键字时,我正在挑战每个关键字类别的想法,以及1,000个关键字表示设计得当的想法。需要更多的信息来评估一个好的答案是什么,所以我刚刚接触了地图关于什么的哲学问题。 – HostileFork 2014-08-28 07:12:46

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(); 
    } 
} 
+0

谢谢你,好像地图容器是要走的路。 – 2014-08-28 21:35:37

+0

@Ben没问题。如果我的帖子或HostileFork的帖子帮助你,你可以选择一个作为答案。 – Foggzie 2014-08-29 21:06:19

+0

我是这个网站的全新品牌,因此对我一无所知。不过,我确实有一个跟进问题给你。因为我打算使用这个映射来通过一个字符串来确定对象的类型,我将与关键值进行比较。我确定了1000个关键字中的哪一个,但是现在我将如何实例化该对象?一旦我确定了对象类型,那么我就会失去接下来要做的事情。由于映射的值只能是单个对象类型,因此我可以在您的示例中为每种类型创建一个函数,但这会破坏使用该映射的目的。有什么建议么? – 2014-09-04 00:44:31

1

两个解决方案:

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

  2. 使用词法分析器,如flex(1)并将关键字构建到.l文件中。这可能会更快,因为它在没有最终查找步骤的情况下一次执行一个字符,但只有当关键字列表预先完全固定时才适用,例如用编程语言。