網站首頁 編程語言 正文
15.9的文本查詢程序是對12.3節的文本查詢程序的擴展,而使用的主要知識也是15章的核心:繼承和多態,即面向對象程序設計。
恩,這一節看的過程中,會有很多不理解。特別是在沒有把整個程序都看完之前,會有很多疑惑,而看完之后,再思考思考,回頭再看本節的前面所寫的程序介紹,會有一些感悟。更加清楚這個程序的原理。
TextQuery.h
#ifndef QUERY_TEXTQUERY_H #define QUERY_TEXTQUERY_H #include<iostream> #include<vector> #include<string> #include<memory> #include<map> #include<set> #include<cstdio> #include<cstdlib> #include<sstream> #include<algorithm> #include<fstream> #include<stack> using namespace std; class QueryResult; // ?????????????????????? class TextQuery { public: using line_no = vector<string>::size_type; TextQuery(ifstream&); // ?????????????????????????????в???????? QueryResult query(const string&) const; // ???????string???????string???в???? private: shared_ptr<vector<string>> file; // ??????? map<string, shared_ptr<set<line_no>>> wm; // ???????????????????????к?set?? }; inline TextQuery::TextQuery(ifstream &is): file(new vector<string>) { string text; while (getline(is, text)) { // ??????е????? file->push_back(text); // ????????е???? int n = file->size() - 1; // ???浱????к???????·?????????????shared_ptr<set<line_no>> ?????????к? istringstream line(text); string word; while(line >> word){ auto &lines = wm[word]; // lines?????shared_ptr if(!lines) // ??????±??????????map?д????????word?????word????????shared_ptr??????shared_ptr???????nullptr; lines.reset(new set<line_no>); lines->insert(n); } } } class QueryResult{ friend ostream& print(ostream& os, const QueryResult& qr); public: QueryResult(string s, shared_ptr<set<TextQuery::line_no>> p, shared_ptr<vector<string>> f) : sought(s), lines(p), file(f) {} auto begin()const {return lines->begin();} auto end()const{return lines->end();} shared_ptr<vector<string>> get_file()const {return file;} private: string sought; // ???????? shared_ptr<set<TextQuery::line_no>> lines; // ??????к? shared_ptr<vector<string>> file; // ??????? }; inline QueryResult TextQuery::query(const string& s)const { static shared_ptr<set<line_no>> nodata(new set<line_no>()); // ?????????????к?set??shared_ptr auto loc = wm.find(s); if(loc == wm.cend()) return QueryResult(s, nodata, file); else return QueryResult(s, loc->second, file); } inline ostream& print(ostream& os, const QueryResult& qr) { os<<qr.sought<<" occurs "<<qr.lines->size()<<" "<<((qr.lines->size()>1)?"times":"time")<<endl; for(const auto& num: *(qr.lines)){ os << "\t(lines "<<num+1<<") "<<(*(qr.file))[num]<<endl; } return os; } inline void runQueries(ifstream& infile) { // infile?????ifstream?????????????????? TextQuery tq(infile); // ???????????????????????????????????????????? while(true) { cout<<"enter word to look for, or q to quit: "<<endl; string s; // ?????????????s=='q'????? if(!(cin>>s) || s == "q") break; print(cout, tq.query(s)) << endl; } } inline void independent_word_query(ifstream& ifile){ vector<string> file; map<string,set<int>> word_map; string text; while(getline(ifile, text)){ file.push_back(text); int n = (int)file.size()-1; // ???push_back?????е??±? istringstream line(text); // ???string text??????????? string word; while(line >> word){ word_map[word].insert(n); } } while(true){ cout << "Enter word to look for, or q to quit: "<<endl; string s; if(!(cin>>s) || s=="q") break; const auto& lines = word_map.find(s); // ?????????????????????pair<string,set<int>> cout<<s<<" occurs "<<(*lines).second.size()<<(lines->second.size()>1?" times":" time")<<endl; for(const auto& i:lines->second){ cout<<"\t(lines "<<i+1<<") "<<file[i]<<endl; } } } #endif
Query.h
#ifndef QUERY_QUERY_H #define QUERY_QUERY_H #include"TextQuery.h" class Query_base { friend class Query; protected: using line_no = TextQuery::line_no; // ???????????eval?????????????????protected?? virtual ~Query_base() = default; private: // eval???????????Query????QueryResult virtual QueryResult eval(const TextQuery&) const = 0; // rep???????????string virtual string rep() const = 0; }; class Query { friend Query operator|(const Query&, const Query&); friend Query operator&(const Query&, const Query&); friend Query operator~(const Query&); public: Query(const string&); // ????????μ?WordQuery public: QueryResult eval(const TextQuery& t)const { return q->eval(t); } string rep()const { return q->rep(); } private: Query(shared_ptr<Query_base> query) :q(query){} shared_ptr<Query_base> q; }; //ostream& operator<<(ostream& os, const Query& q) //{ // // Query::rep???????Query_base????rep??????????? // return os<<q.rep(); //} class WordQuery: public Query_base { friend Query; private: WordQuery(const string& s): query_word(s) {} virtual QueryResult eval(const TextQuery& t)const override { return t.query(query_word); } virtual string rep() const override { return query_word; } string query_word; }; inline Query::Query(const string& s): q(new WordQuery(s)) {} // ??Query????????????·??????? // Query q = ~Query("dog"); class NotQuery: public Query_base { friend Query operator~(const Query&); private: NotQuery(const Query& q): query(q) {} virtual QueryResult eval(const TextQuery&) const override; virtual string rep() const override { return "~("+query.rep()+")"; } Query query; }; inline Query operator~(const Query &operand) { return Query(shared_ptr<Query_base>(new NotQuery(operand))); } class BinaryQuery: public Query_base { protected: BinaryQuery(const Query& l, const Query &r, string s): lhs(l), rhs(r), opSym(s) {} // ???????BinaryQuery??????eval; virtual string rep()const override { return "(" + lhs.rep() + " " + opSym + + " " + rhs.rep() + ")"; } // ???????rep?????????? Query lhs, rhs; string opSym; }; class AndQuery: public BinaryQuery { friend Query operator&(const Query&, const Query&); private: AndQuery(const Query& left, const Query& right): BinaryQuery(left,right,"&") {} // ?????rep????????eval QueryResult eval(const TextQuery&) const override; }; inline Query operator&(const Query &lhs, const Query &rhs) { return Query (shared_ptr<Query_base>(new AndQuery(lhs,rhs))); } class OrQuery: public BinaryQuery { friend Query operator|(const Query&, const Query&); private: OrQuery(const Query& left, const Query &right): BinaryQuery(left,right,"|") {} QueryResult eval(const TextQuery&)const override; }; inline Query operator|(const Query &lhs, const Query &rhs) { return Query(shared_ptr<Query_base>(new OrQuery(lhs,rhs))); } #endif //QUERY_QUERY_H
Query.cpp
#include "Query.h" // Query q = Query("dog") | Query("cat"); QueryResult OrQuery::eval(const TextQuery& t) const { auto left = lhs.eval(t), right = rhs.eval(t); shared_ptr<set<line_no>> ret_lines(new set<line_no>(left.begin(),left.end())); ret_lines->insert(right.begin(),right.end()); return QueryResult(rep(),ret_lines,lhs.eval(t).get_file()); } QueryResult AndQuery::eval(const TextQuery& text) const { auto left = lhs.eval(text), right = rhs.eval(text); auto ret_lines = make_shared<set<line_no>>(); // ????set?????????????????????? set_intersection(left.begin(),left.end(),right.begin(),right.end(),inserter(*ret_lines,ret_lines->begin())); return QueryResult(rep(),ret_lines,left.get_file()); } QueryResult NotQuery::eval(const TextQuery& text) const { auto result = query.eval(text); auto ret_lines = make_shared<set<line_no>>(); auto beg = result.begin(), end = result.end(); auto sz = result.get_file()->size(); for(size_t n = 0; n != sz; ++n){ if(beg == end || *beg != n) ret_lines->insert(n); else ++beg; } return QueryResult(rep(), ret_lines, result.get_file()); }
main.cpp
void read_file(ifstream &f){ TextQuery textquery(f); Query q ( Query("dog") & Query("cat")); print(cout,q.eval(textquery))<<endl; Query q2 = Query("bbb"); print(cout, q2.eval(textquery))<<endl; Query q3 = ~Query("dog"); print(cout, q3.eval(textquery))<<endl; system("pause"); } int main(){ string filename; cout<<"Please enter filename"<<endl; cin >> filename; ifstream file(filename); if(!file){ cout<<"Filename error"<<endl; return -1; } read_file(file); return 0; }
出現了一些意外,代碼中中文注釋都是亂碼。
針對程序所涉及的幾個類的介紹和理解:
TextQuery類:
可以把每個TextQuery類對象看作一個文本文件,這個類將某個文本文件的內容保存在一個vector<string>中,并保存了每個單詞對應的行號,而query函數就是接收一個string,然后查找這個單詞。而這里的返回結果是一個QueryResult類對象。這個類只是用來保存一個查詢結果,其實后續的& | ~的結果也都是這個QueryResult類對象
在12章時,我想過,為什么要設計這么一個類呢?如果直接在query函數中實現查找并打印不可以嗎?其實這樣是不太合適的,一個最直接的原因就是,在后方進行word1 & word2操作時,不方便,封裝一個查詢結果類更容易處理。這樣,也可以支持更多的操作,而不僅僅是打印。
QueryResult類:
表示一個查詢結果,通常與print函數聯系起來使用,print用于打印這個查詢結果。
后續的就是一些新的繼承方面的類了,也就是為了支持word1 & word2 或者 word1 | word2 或者 ~word操作。而這些查詢都建模成了相互獨立的類,即AndQuery OrQuery NotQuery 而最基本的還有一個WordQuery,這些類都繼承自一個抽象基類Query_base。
Query_base類:
最主要的就是兩個成員函數:eval 和 rep,說真的,我覺得這兩個名字起的并不好,當然受限于我的英文水平,其實eval就相當于TextQuery類的query函數,參數是TextQuery,即一個文本文件,然后在這個文本文件中執行查詢操作,返回一個查詢結果QueryResult。rep函數用于返回查詢的string表示形式,比如~(word1 & word2)。
Query類:
這個類是很重要的,當然這句話是句廢話.... 這個類的數據成員是一個基類的指針,而這也是這個程序支持面向對象編程和多態的根本原因。
這是一個接口類,它的成員函數仍然是eval和rep,調用的是基類指針所指向對象的eval和rep,基類指針或引用調用虛函數發生動態綁定。所以,Query類基類指針指向的對象,可能是繼承體系中任何一種類型的對象。比如: Query q = Query("dog") & Query("cat"); 而這里的q的基類指針指向的就是一個AndQuery類的對象,調用的eval和rep也都是AndQuery類版本的eval和rep,而這個AndQuery類的數據成員就包括著右邊&運算符左右兩邊的兩個WordQuery類的對象,這里是使用了&運算符重載。operator& 返回的就是一個基類指針綁定到AndQuery類對象的Query類對象。返回值用于初始化q。這里調用的應該是Query類的拷貝構造函數吧
WordQuery類:
Query_base類的派生類,表示對于某個單詞最直接的查詢,覆蓋了eval和rep,為什么說eval相當于query呢?這里的eval就是最明顯的證明:這里的eval直接返回參數TextQuery類的query結果,就是對某個單詞的查詢結果。而后方的Not And Or,都沒有調用這個query操作,他們操作的是Query類對象的查詢結果。
NotQuery類:
這個類也是Query_base的派生類,表示~查詢方式。~運算符重載之后,返回的就是一個綁定到NotQuery類對象上的Query類對象,而~作用的就是另一個Query類對象的eval查詢結果。
AndQuery OrQuery類:
因為這兩個類都操作兩個Query類對象,所以又實現了一個BinaryQuery抽象基類,這個基類繼承自Query_base,多了兩個Query類對象的成員,以及一個操作符成員,用于表示& 還是 |。
這兩個類所關聯的是& |運算符,operator& 返回的分別是是基類指針綁定到AndQuery類對象上的Query類對象 。operator | 返回的是基類指針綁定到OrQuery類對象上的Query類對象,而這兩個類的rep函數很簡單,對于兩個成員的rep函數進行一些簡單加工即可,而eval函數,參數仍然是一個TextQuery類,在兩個Query成員返回的QueryResult上進行處理,然后返回一個新的QueryResult對象。代表著一種& 或者 |操作之后的查詢結果。
還有一個比較有趣的是:
如下代碼:
Query q = Query("Dog") & Query("Cat") | Query("Bird");
Print( cout, q.eval(textquery) );
一共創建了三個WordQuery,一個AndQuery,一個OrQuery。先后順序不太清楚。。。但是其實創建好q對象之后,這里面并沒有什么查詢結果,保存的只是這些單詞,還有一些沒有調用的成員函數eval和rep。
根據運算符優先級規則,q是一個基類指針指向OrQuery類對象的Query對象,而如果想打印出這個查詢結果,必然是要調用eval函數的,參數表示,在這個文件里查找這三個單詞。在OrQuery的eval調用的最開始,兩個Query類對象數據成員的查詢結果還沒有出來,而在eval函數內部,計算了兩個查詢結果,一個是rhs數據成員的對Bird單詞的查詢,查詢的位置就是那個textquery保存的文件內容。另一個是AndQuery的eval函數的返回結果,這個結果是對兩個WordQuery類對象查詢結果的&操作之后的結果。最后才對這兩個QueryResult結果進行合并處理。然后返回一個新的查詢結果。
現在看來,只有WordQuery類對象調用了TextQuery的query操作。而其余的Or Not And都是對其他的Query對象的查詢結果進行加工。當然這些eval函數的參數都是同一個TextQuery。并且都是返回的QueryResult。
說真的,之前比較疑惑的是,我感覺這些eval函數的TextQuery參數的傳遞有些奇怪。對比之前12章的文本查詢程序,最后封裝的對文件的查詢的函數,看上去就舒服多了,它是把ifstream類對象傳遞給TextQuery類的構造函數的參數,然后后面調用query函數進行查詢,返回一個QueryResult對象。調用print函數打印。
但是這里再探的程序就有點不一樣和奇怪了,你也可以封裝一個完整的查詢函數,但是如果不那樣做的話,進行的操作就是。
Query q = Query("Dog") & Query("Cat") | Query("Bird");
Print( cout, q.eval(textquery) );
這種操作,相比于 TextQuery tq(ifile); print(cout, tq.query("Dog"));
就有點奇怪了。
上方的main.cpp主函數,并沒有實現完善的查詢函數,即實時查詢操作,如輸入Dog & Cat | Bird。然后打印查詢結果,之后有能力實現的話可能會補上。
就以這篇文章作為大一學習生活的結束吧。
原文鏈接:https://blog.csdn.net/i777777777777777/article/details/125343846
相關推薦
- 2022-06-26 對ASP.Net的WebAPI項目進行測試_實用技巧
- 2022-05-02 numpy中np.dstack()、np.hstack()、np.vstack()用法_python
- 2023-12-24 mybatis-plus代碼生成器使用(generator)
- 2022-07-02 Python使用struct庫的用法小結_python
- 2022-05-25 python3?字符串str和bytes相互轉換_python
- 2023-02-01 C語言中聯合體與共用體和枚舉使用語法示例_C 語言
- 2022-05-26 Android?Flutter實現3D動畫效果示例詳解_Android
- 2022-08-06 python實現去除空格及tab換行符的方法_python
- 最近更新
-
- window11 系統安裝 yarn
- 超詳細win安裝深度學習環境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優雅實現加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發現-Nac
- Spring Security之基于HttpR
- Redis 底層數據結構-簡單動態字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支