C++詳解Primer文本查詢程序的實(shí)現(xiàn)
15.9的文本查詢程序是對(duì)12.3節(jié)的文本查詢程序的擴(kuò)展,而使用的主要知識(shí)也是15章的核心:繼承和多態(tài),即面向?qū)ο蟪绦蛟O(shè)計(jì)。
恩,這一節(jié)看的過程中,會(huì)有很多不理解。特別是在沒有把整個(gè)程序都看完之前,會(huì)有很多疑惑,而看完之后,再思考思考,回頭再看本節(jié)的前面所寫的程序介紹,會(huì)有一些感悟。更加清楚這個(gè)程序的原理。
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; }
出現(xiàn)了一些意外,代碼中中文注釋都是亂碼。
針對(duì)程序所涉及的幾個(gè)類的介紹和理解:
TextQuery類:
可以把每個(gè)TextQuery類對(duì)象看作一個(gè)文本文件,這個(gè)類將某個(gè)文本文件的內(nèi)容保存在一個(gè)vector<string>中,并保存了每個(gè)單詞對(duì)應(yīng)的行號(hào),而query函數(shù)就是接收一個(gè)string,然后查找這個(gè)單詞。而這里的返回結(jié)果是一個(gè)QueryResult類對(duì)象。這個(gè)類只是用來保存一個(gè)查詢結(jié)果,其實(shí)后續(xù)的& | ~的結(jié)果也都是這個(gè)QueryResult類對(duì)象
在12章時(shí),我想過,為什么要設(shè)計(jì)這么一個(gè)類呢?如果直接在query函數(shù)中實(shí)現(xiàn)查找并打印不可以嗎?其實(shí)這樣是不太合適的,一個(gè)最直接的原因就是,在后方進(jìn)行word1 & word2操作時(shí),不方便,封裝一個(gè)查詢結(jié)果類更容易處理。這樣,也可以支持更多的操作,而不僅僅是打印。
QueryResult類:
表示一個(gè)查詢結(jié)果,通常與print函數(shù)聯(lián)系起來使用,print用于打印這個(gè)查詢結(jié)果。
后續(xù)的就是一些新的繼承方面的類了,也就是為了支持word1 & word2 或者 word1 | word2 或者 ~word操作。而這些查詢都建模成了相互獨(dú)立的類,即AndQuery OrQuery NotQuery 而最基本的還有一個(gè)WordQuery,這些類都繼承自一個(gè)抽象基類Query_base。
Query_base類:
最主要的就是兩個(gè)成員函數(shù):eval 和 rep,說真的,我覺得這兩個(gè)名字起的并不好,當(dāng)然受限于我的英文水平,其實(shí)eval就相當(dāng)于TextQuery類的query函數(shù),參數(shù)是TextQuery,即一個(gè)文本文件,然后在這個(gè)文本文件中執(zhí)行查詢操作,返回一個(gè)查詢結(jié)果QueryResult。rep函數(shù)用于返回查詢的string表示形式,比如~(word1 & word2)。
Query類:
這個(gè)類是很重要的,當(dāng)然這句話是句廢話.... 這個(gè)類的數(shù)據(jù)成員是一個(gè)基類的指針,而這也是這個(gè)程序支持面向?qū)ο缶幊毯投鄳B(tài)的根本原因。
這是一個(gè)接口類,它的成員函數(shù)仍然是eval和rep,調(diào)用的是基類指針?biāo)赶驅(qū)ο蟮膃val和rep,基類指針或引用調(diào)用虛函數(shù)發(fā)生動(dòng)態(tài)綁定。所以,Query類基類指針指向的對(duì)象,可能是繼承體系中任何一種類型的對(duì)象。比如: Query q = Query("dog") & Query("cat"); 而這里的q的基類指針指向的就是一個(gè)AndQuery類的對(duì)象,調(diào)用的eval和rep也都是AndQuery類版本的eval和rep,而這個(gè)AndQuery類的數(shù)據(jù)成員就包括著右邊&運(yùn)算符左右兩邊的兩個(gè)WordQuery類的對(duì)象,這里是使用了&運(yùn)算符重載。operator& 返回的就是一個(gè)基類指針綁定到AndQuery類對(duì)象的Query類對(duì)象。返回值用于初始化q。這里調(diào)用的應(yīng)該是Query類的拷貝構(gòu)造函數(shù)吧
WordQuery類:
Query_base類的派生類,表示對(duì)于某個(gè)單詞最直接的查詢,覆蓋了eval和rep,為什么說eval相當(dāng)于query呢?這里的eval就是最明顯的證明:這里的eval直接返回參數(shù)TextQuery類的query結(jié)果,就是對(duì)某個(gè)單詞的查詢結(jié)果。而后方的Not And Or,都沒有調(diào)用這個(gè)query操作,他們操作的是Query類對(duì)象的查詢結(jié)果。
NotQuery類:
這個(gè)類也是Query_base的派生類,表示~查詢方式。~運(yùn)算符重載之后,返回的就是一個(gè)綁定到NotQuery類對(duì)象上的Query類對(duì)象,而~作用的就是另一個(gè)Query類對(duì)象的eval查詢結(jié)果。
AndQuery OrQuery類:
因?yàn)檫@兩個(gè)類都操作兩個(gè)Query類對(duì)象,所以又實(shí)現(xiàn)了一個(gè)BinaryQuery抽象基類,這個(gè)基類繼承自Query_base,多了兩個(gè)Query類對(duì)象的成員,以及一個(gè)操作符成員,用于表示& 還是 |。
這兩個(gè)類所關(guān)聯(lián)的是& |運(yùn)算符,operator& 返回的分別是是基類指針綁定到AndQuery類對(duì)象上的Query類對(duì)象 。operator | 返回的是基類指針綁定到OrQuery類對(duì)象上的Query類對(duì)象,而這兩個(gè)類的rep函數(shù)很簡(jiǎn)單,對(duì)于兩個(gè)成員的rep函數(shù)進(jìn)行一些簡(jiǎn)單加工即可,而eval函數(shù),參數(shù)仍然是一個(gè)TextQuery類,在兩個(gè)Query成員返回的QueryResult上進(jìn)行處理,然后返回一個(gè)新的QueryResult對(duì)象。代表著一種& 或者 |操作之后的查詢結(jié)果。
還有一個(gè)比較有趣的是:
如下代碼:
Query q = Query("Dog") & Query("Cat") | Query("Bird");
Print( cout, q.eval(textquery) );
一共創(chuàng)建了三個(gè)WordQuery,一個(gè)AndQuery,一個(gè)OrQuery。先后順序不太清楚。。。但是其實(shí)創(chuàng)建好q對(duì)象之后,這里面并沒有什么查詢結(jié)果,保存的只是這些單詞,還有一些沒有調(diào)用的成員函數(shù)eval和rep。
根據(jù)運(yùn)算符優(yōu)先級(jí)規(guī)則,q是一個(gè)基類指針指向OrQuery類對(duì)象的Query對(duì)象,而如果想打印出這個(gè)查詢結(jié)果,必然是要調(diào)用eval函數(shù)的,參數(shù)表示,在這個(gè)文件里查找這三個(gè)單詞。在OrQuery的eval調(diào)用的最開始,兩個(gè)Query類對(duì)象數(shù)據(jù)成員的查詢結(jié)果還沒有出來,而在eval函數(shù)內(nèi)部,計(jì)算了兩個(gè)查詢結(jié)果,一個(gè)是rhs數(shù)據(jù)成員的對(duì)Bird單詞的查詢,查詢的位置就是那個(gè)textquery保存的文件內(nèi)容。另一個(gè)是AndQuery的eval函數(shù)的返回結(jié)果,這個(gè)結(jié)果是對(duì)兩個(gè)WordQuery類對(duì)象查詢結(jié)果的&操作之后的結(jié)果。最后才對(duì)這兩個(gè)QueryResult結(jié)果進(jìn)行合并處理。然后返回一個(gè)新的查詢結(jié)果。
現(xiàn)在看來,只有WordQuery類對(duì)象調(diào)用了TextQuery的query操作。而其余的Or Not And都是對(duì)其他的Query對(duì)象的查詢結(jié)果進(jìn)行加工。當(dāng)然這些eval函數(shù)的參數(shù)都是同一個(gè)TextQuery。并且都是返回的QueryResult。
說真的,之前比較疑惑的是,我感覺這些eval函數(shù)的TextQuery參數(shù)的傳遞有些奇怪。對(duì)比之前12章的文本查詢程序,最后封裝的對(duì)文件的查詢的函數(shù),看上去就舒服多了,它是把ifstream類對(duì)象傳遞給TextQuery類的構(gòu)造函數(shù)的參數(shù),然后后面調(diào)用query函數(shù)進(jìn)行查詢,返回一個(gè)QueryResult對(duì)象。調(diào)用print函數(shù)打印。
但是這里再探的程序就有點(diǎn)不一樣和奇怪了,你也可以封裝一個(gè)完整的查詢函數(shù),但是如果不那樣做的話,進(jìn)行的操作就是。
Query q = Query("Dog") & Query("Cat") | Query("Bird");
Print( cout, q.eval(textquery) );
這種操作,相比于 TextQuery tq(ifile); print(cout, tq.query("Dog"));
就有點(diǎn)奇怪了。
上方的main.cpp主函數(shù),并沒有實(shí)現(xiàn)完善的查詢函數(shù),即實(shí)時(shí)查詢操作,如輸入Dog & Cat | Bird。然后打印查詢結(jié)果,之后有能力實(shí)現(xiàn)的話可能會(huì)補(bǔ)上。
就以這篇文章作為大一學(xué)習(xí)生活的結(jié)束吧。
到此這篇關(guān)于C++詳解Primer文本查詢程序的實(shí)現(xiàn)的文章就介紹到這了,更多相關(guān)C++文本查詢內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
通過C++程序示例理解設(shè)計(jì)模式中的外觀模式
這篇文章主要介紹了通過設(shè)計(jì)模式中的外觀模式及相關(guān)的C++程序示例,外觀模式在高層提供了一個(gè)統(tǒng)一的接口實(shí)現(xiàn)一定程度上的解耦,需要的朋友可以參考下2016-03-03C++中簡(jiǎn)單讀寫文本文件的實(shí)現(xiàn)方法
本篇文章是對(duì)C++中簡(jiǎn)單讀寫文本文件的方法進(jìn)行了詳細(xì)的分析介紹,需要的朋友參考下2013-05-05一文帶你入木三分地理解字符串KMP算法以及C++實(shí)現(xiàn)
KMP算法是一種改進(jìn)的字符串匹配算法,KMP算法的核心是利用匹配失敗后的信息,盡量減少模式串與主串的匹配次數(shù)以達(dá)到快速匹配的目的。本文就來和大家聊聊KMP算法的原理與實(shí)現(xiàn),需要的可以參考一下2022-12-12C++中std::construct()與std::destroy()的使用
std::construct()和std::destroy()是C++ STL中的函數(shù)模板,用于在已分配的存儲(chǔ)區(qū)域中構(gòu)造或銷毀對(duì)象,本文主要介紹了C++中std::construct()與std::destroy()的使用,感興趣的可以了解一下2024-02-02基于C語言實(shí)現(xiàn)簡(jiǎn)單的走迷宮游戲
這篇文章主要介紹了基于C語言實(shí)現(xiàn)簡(jiǎn)單的走迷宮游戲,用到雙向隊(duì)列,方便在運(yùn)行完畢后輸出經(jīng)過的點(diǎn),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2016-04-04C語言?超詳細(xì)介紹與實(shí)現(xiàn)線性表中的無頭單向非循環(huán)鏈表
無頭單向非循環(huán)鏈表:結(jié)構(gòu)簡(jiǎn)單,一般不會(huì)單獨(dú)用來存數(shù)據(jù)。實(shí)際中更多是作為其他數(shù)據(jù)結(jié)構(gòu)的子結(jié)構(gòu),如哈希桶、圖的鄰接表等等。另外這種結(jié)構(gòu)在筆試面試中出現(xiàn)很多2022-03-03C語言中關(guān)于動(dòng)態(tài)內(nèi)存分配的詳解
動(dòng)態(tài)內(nèi)存是指在堆上分配的內(nèi)存,而靜態(tài)內(nèi)存是指在棧上分配的內(nèi)存。棧上分配的內(nèi)存是由系統(tǒng)分配和釋放的,空間有限,在復(fù)合語句或函數(shù)運(yùn)行結(jié)束后就會(huì)被系統(tǒng)自動(dòng)釋放而堆上分配的內(nèi)存則不會(huì)有這個(gè)問題。2021-09-09