2014-08-28 70 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文件中。這可能會更快,因爲它在沒有最終查找步驟的情況下一次執行一個字符,但只有當關鍵字列表預先完全固定時才適用,例如用編程語言。