笔记
重点在于理解Query_base 的继承体系,派生类有WordQuery ,NotQuery ,AndQuery ,OrQuery 。并且创建一个Query 类来作为Query_base 的接口类,通过Query 类对外界隐藏Query_base 继承体系,更加方便的管理Query_base 派生类直接的转换
C++Prime习题集解答
书中的实现方式是用Query 类封装了Query_base 指针,管理实际查询处理用到的不同Query 类型对象.如果不使用Query 类,则设计使用Query 的地方,都要改为Query_base 指针。如创建单个词查询,就必须创建WordQuery 类而不是Query 类,几个重载的布尔运算符也不能针对Query ,而需针对Query_base 指针,将结果赋予Query_base 指针,然后再进行下一步操作,资源管理方面也需要重新设计,因此当前设计仍是最佳方式
类图:
代码(建议自己敲一遍)
#pragma once
#ifndef QUERY_H
#define QUERY_H
#include<memory>
#include<vector>
#include<string>
#include<map>
#include<set>
#include<istream>
#include<fstream>
#include<iostream>
#include<algorithm>
#include<sstream>
using std::shared_ptr;
using std::vector;
using std::string;
using std::map;
using std::set;
using std::ifstream;
using std::ostream;
using std::cout;
using std::endl;
using std::istringstream;
using std::make_shared;
class QueryResult {
public:
typedef vector<string>::size_type line_no;
friend ostream& print(ostream&, const QueryResult&);
friend ostream& operator<<(ostream&, const QueryResult&);
QueryResult(const string& w,shared_ptr<set<line_no>> n,shared_ptr<vector<string>> i):word(w),nos(n),input(i){}
set<line_no>::iterator begin()const{
return nos->begin();
}
set<line_no>::iterator end()const {
return nos->end();
}
shared_ptr<vector<string>> get_file()const{
return input;
}
private:
string word;
shared_ptr<set<line_no>> nos;
shared_ptr<vector<string>> input;
};
class TextQuery{
public:
typedef vector<string>::size_type line_no;
TextQuery(ifstream&);
QueryResult query(const string&)const;
private:
shared_ptr<vector<string>> input;
map<string, shared_ptr<set<line_no>>> result;
};
ostream& print(ostream& os, const QueryResult& qr) {
os << qr.word << " occurs: " << qr.nos->size() << (qr.nos->size() > 1 ? "times" : "time" )<< endl;
for (int i : *qr.nos) {
cout << "\t(line " << i + 1 << ") " << qr.input->at(i) << endl;
}
return os;
}
TextQuery::TextQuery(ifstream& ifs) :input(new vector<string>) {
line_no lineNo = 0;
for (string line; getline(ifs, line); lineNo++) {
input->push_back(line);
istringstream line_stream(line);
string text, word;
for (; line_stream >> text; word.clear()) {
remove_copy_if(text.begin(), text.end(), std::back_inserter(word), ispunct);
auto& nos = result[word];
if (!nos)nos.reset(new set<line_no>);
nos->insert(lineNo);
}
}
}
QueryResult TextQuery::query(const string& word) const {
static shared_ptr<set<line_no>> nos(new set<line_no>);
auto found = result.find(word);
if (found==result.end()) {
return QueryResult(word, nos, input);
}
else {
return QueryResult(word, found->second,input);
}
}
class Query_base {
friend class Query;
protected:
using line_no = TextQuery::line_no;
virtual ~Query_base() = default;
private:
virtual QueryResult eval(const TextQuery&)const = 0;
virtual string rep()const = 0;
};
class Query {
friend Query operator&(const Query&,const Query&);
friend Query operator~(const Query&);
friend Query operator|(const Query&, const Query&);
public:
Query(const string& word);
string rep()const{
return q->rep();
}
QueryResult eval(const TextQuery& tq)const {
return q->eval(tq);
}
private:
Query(shared_ptr<Query_base> query) :q(query) { cout << "调用 Query ctor" << endl; };
shared_ptr<Query_base> q;
};
ostream& operator<<(ostream& os,const Query& query) {
return os << query.rep() << endl;
}
ostream& operator<<(ostream& os, const QueryResult& qr) {
os << qr.word << " occurs: " << qr.nos->size() << (qr.nos->size() > 1 ? "times" : "time") << endl;
for (int i : *qr.nos) {
cout << "\t(line " << i + 1 << ") " << qr.input->at(i) << endl;
}
return os;
}
class WordQuery :public Query_base {
friend class Query;
WordQuery(const string& s) :query_word(s) { cout << "调用 wordQuery ctor" << endl; }
string rep() const override{
return query_word;
}
QueryResult eval(const TextQuery& tq)const override {
return tq.query(query_word);
}
string query_word;
};
inline
Query::Query(const string& word) :q(new WordQuery(word)) { cout << "调用 Query ctor" << endl; };
class NotQuery : public Query_base {
friend Query operator~(const Query&);
NotQuery(const Query &q) :query(q){ cout << "调用 NotQuery ctor" << endl; }
string rep()const override{
return "~("+query.rep()+")";
}
QueryResult eval(const TextQuery&tq)const override {
auto result = query.eval(tq);
auto ret_lines = make_shared<set<line_no>>();
auto beg = result.begin(), end = result.end();
line_no sz = result.get_file()->size();
for (size_t n = 0; n != sz; n++) {
if (beg == end || *beg != n)
ret_lines->insert(n);
else if(beg!=end)
beg++;
}
return QueryResult(rep(), ret_lines, result.get_file());
}
Query query;
};
inline
Query operator~(const Query& query) {
return shared_ptr<Query_base>(new NotQuery(query));
}
class BinaryQuery :public Query_base {
protected:
BinaryQuery(const Query&l,const Query&r,const string& op):lhs(l),rhs(r),opSym(op){ cout << "调用 BinaryQuery ctor" << endl; }
string rep()const override{
return lhs.rep() + opSym + rhs.rep();
}
Query lhs, rhs;
string opSym;
};
class AndQuery :public BinaryQuery {
friend Query operator&(const Query&, const Query&);
AndQuery(const Query& l, const Query& r) :BinaryQuery(l, r, "&") { cout << "调用 AndQuery ctor" << endl; };
QueryResult eval(const TextQuery& tq)const override {
auto left = lhs.eval(tq), right = rhs.eval(tq);
auto ret_line = make_shared<set<line_no>>();
set_intersection(left.begin(), left.end(), right.begin(), right.end(), inserter(*ret_line, ret_line->begin()));
return QueryResult(rep(), ret_line, left.get_file());
}
};
inline
Query operator&(const Query& lhs, const Query& rhs) {
return shared_ptr<Query_base>(new AndQuery(lhs,rhs));
}
class OrQuery:public BinaryQuery {
friend Query operator|(const Query&, const Query&);
OrQuery(const Query& l, const Query& r) :BinaryQuery(l, r, "|") { cout << "调用 OrQuery ctor" << endl; };
QueryResult eval(const TextQuery& tq)const override {
auto left = lhs.eval(tq), right = rhs.eval(tq);
auto ret_line = make_shared<set<line_no>>(left.begin(), left.end());
ret_line->insert(right.begin(), right.end());
return QueryResult(rep(),ret_line,left.get_file());
}
};
inline
Query operator|(const Query& lhs, const Query& rhs) {
return shared_ptr<Query_base>(new OrQuery(lhs, rhs));
}
#endif
主函数
#include"Query.h"
#include<iostream>
using std::cout;
using std::cin;
string fileName = "./testFile/test15.9.4.txt";
void runQuery(ifstream& ifs,Query& q) {
TextQuery tq(ifs);
cout << q;
cout << q.eval(tq);
}
int main(int argc, char** argv) {
ifstream ifs(fileName);
Query q = Query("fiery") & Query("bird") | Query("wind");
if (ifs)
runQuery(ifs,q);
else
std::cerr << "Cannot open this file!" << fileName;
}
教材所提供的查询文段
Alice Emma has long flowing red hair.
Her Daddy says when the wind blows
through her hair, it looks almost alive,
like a fiery bird in flight.
A beautiful fiery bird, he tells her,
magical but untamed.
"Daddy, shush, there is no such thing,"
she tells him, at the same time wanting
him to tell her more.
Shyly, she saks, "I mean,Daddy, is there?"
|