- 单词查询的基础上实现3个逻辑查询
~
得到所有不匹配的行&
将2个匹配结果取交集|
将2个匹配结果取并集
- 表达式查询:
Query("xx") & Query("xx") | Query("xx")
如何抽象出基类 且 定义接口类
TextQuery
已经实现了单词查询,如果剩下的3个操作通过TextQuery
派生.但是当2个对象进行&和|操作时,我们又要定义 派生类结果的&(交集)和|(并集)的函数- 将不同的操作定义成不同的类,共享一个基类,并且使用基类的
eval()
函数这个公共接口.
WordQuery
NotQuery
AndQuery
OrQuery
- 建立4个类封装各自的
eval()
函数得到结果,接收TextQuery
,返回QueryResult
,
WordQuery
:单词查询,实际调用的是TextQuery::query()
NotQuery
:一个运算对象,AndQuery
:2个运算对象,在WordQuery
基础上交结果OrQuery
:2个运算对象,在WordQuery
基础上并结果
- 由于
AndQuery
和OrQuery
有2个运算对象,那么不妨抽象出一个基类BinaryQuery
将2个对象保存在此类中. - 4个对象都具有
eval
函数,创建一个抽象基类(Query_base
)来表示该接口,并且将eval
定义成虚函数,所有特殊查询类需要重写该函数.通过一个抽象基类包含一个基类指针来存子类对象实现对eval函数的虚调用 - 但是该基类只作为
eval
函数的接口,而我们只想通过一个类来完成所有操作,那么将Query
作为一个接口类,将eval
这个函数接口封装一下
并且接口类通过Query_base
指针绑定到Query_base
的不同派生类对象上
&
将Query
对象绑定到AndQuery
对象上|
,~
同理
- 通过接口类能够实现支持不同的运算符和派生类对象的转换
体会Query
的作用,
test.h
#ifndef TEXT_H
#define TEXT_H
#include <iostream>
#include <string>
#include <vector>
#include <map>
#include <set>
#include <iterator>
#include <memory>
#include <fstream>
#include <sstream>
#include <algorithm>
class QueryResult {
friend std::ostream& print(std::ostream&,const QueryResult&);
public:
using line_no = std::vector<std::string>::size_type;
using line_it = std::set<line_no>::const_iterator;
QueryResult(std::string s,
std::shared_ptr<std::set<line_no>> p,
std::shared_ptr<std::vector<std::string>> f):
sought(s),lines(p),file(f) {}
std::set<line_no>::size_type size() const {return lines->size();}
line_it begin() const { return lines->begin(); }
line_it end() const { return lines->end(); }
std::shared_ptr<std::vector<std::string>>
get_file() { return file; }
private:
std::string sought;
std::shared_ptr<std::set<line_no>> lines;
std::shared_ptr<std::vector<std::string>> file;
};
class TextQuery {
public:
using line_no = std::vector<std::string>::size_type;
TextQuery(std::ifstream&);
QueryResult query(const std::string&) const;
private:
std::shared_ptr<std::vector<std::string>> file;
std::map<std::string,std::shared_ptr<std::set<line_no>>> wm;
};
TextQuery::TextQuery(std::ifstream& is):file(new std::vector<std::string>) {
std::string text;
while (getline(is,text)) {
file->push_back(text);
int n = file->size() - 1;
std::istringstream line(text);
std::string word;
while (line >> word) {
auto& lines = wm[word];
if (!lines)
lines.reset(new std::set<line_no>);
lines->insert(n);
}
}
}
QueryResult TextQuery::query(const std::string& sought) const {
static std::shared_ptr<std::set<line_no>> nodata = std::make_shared<std::set<line_no>>();
auto loc = wm.find(sought);
if (loc == wm.end())
return QueryResult(sought,nodata,file);
else
return QueryResult(sought,loc->second,file);
}
std::ostream& print(std::ostream& os,const QueryResult& res) {
os << res.sought << " occurs " << res.lines->size() << " \n";
for (auto i: *res.lines)
os << " line" << i+1 << ": " << *(res.file->begin() + i) << "\n";
return os;
}
// start to achieve inherience
class Query_base {
friend class Query;
public:
using line_no = TextQuery::line_no;
virtual ~Query_base() {}
private:
virtual QueryResult eval(const TextQuery&) const =0;
virtual std::string rep() const =0;
};
class Query {
friend Query operator~(const Query&);
friend Query operator&(const Query&,const Query&);
friend Query operator|(const Query&,const Query&);
public:
Query(const std::string&);
QueryResult eval(const TextQuery& t) const { return q->eval(t); }
std::string rep() const { return q->rep(); }
private:
Query(std::shared_ptr<Query_base> query):q(query) {}
std::shared_ptr<Query_base> q;
};
std::ostream& operator<<(std::ostream& os,const Query& query) { return os<<query.rep(); }
class WordQuery:public Query_base {
friend class Query;
WordQuery(const std::string& s):query_word(s) {}
QueryResult eval(const TextQuery& t) const { return t.query(query_word); }
std::string rep() const { return query_word; }
std::string query_word;
};
inline Query::Query(const std::string& s):q(new WordQuery(s)) {}
class NotQuery:public Query_base {
friend Query operator~(const Query&);
NotQuery(const Query& q):query(q) {}
QueryResult eval(const TextQuery& t) const;
std::string rep() const { return "~("+query.rep()+")";}
Query query;
};
inline Query operator~(const Query& operand) {
return std::shared_ptr<Query_base>(new NotQuery(operand));
}
class BinaryQuery:public Query_base {
protected:
BinaryQuery(const Query& l,const Query& r,std::string s):
lhs(l),rhs(r),opSym(s) {}
std::string rep() const { return "("+lhs.rep()+" "+opSym+" "+rhs.rep()+")"; }
Query lhs,rhs;
std::string opSym;
};
class AndQuery:public BinaryQuery {
friend Query operator&(const Query&,const Query&);
AndQuery(const Query& left,const Query& right):BinaryQuery(left,right,"&") {}
QueryResult eval(const TextQuery&) const;
};
inline Query operator&(const Query& left,const Query& right) {
return std::shared_ptr<Query_base>(new AndQuery(left,right));
}
class OrQuery:public BinaryQuery {
friend Query operator|(const Query&,const Query&);
OrQuery(const Query& left,const Query& right):BinaryQuery(left,right,"|") {}
QueryResult eval(const TextQuery&) const;
};
inline Query operator|(const Query& left,const Query& right) {
return std::shared_ptr<Query_base>(new OrQuery(left,right));
}
QueryResult OrQuery::eval(const TextQuery& text) const {
auto right = rhs.eval(text),left = lhs.eval(text);
auto ret_lines = std::make_shared<std::set<line_no>> (left.begin(),left.end());
ret_lines->insert(right.begin(),right.end());
return QueryResult(rep(),ret_lines,left.get_file());
}
QueryResult AndQuery::eval(const TextQuery& text) const {
auto left=lhs.eval(text),right=rhs.eval(text);
auto ret_lines = std::make_shared<std::set<line_no>>();
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 = std::make_shared<std::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());
}
#endif
test.cpp
- 通过
vector<array<string,3>>
还实现了对历史操作的查询功能
#include <iostream>
#include "test.h"
#include <fstream>
#include <vector>
#include <iterator>
using namespace std;
bool get_word(string& s1) {
cout << "enter a word to search for,or q to quit, or h to history: ";
cin >> s1;
if (!cin || s1=="q") return 0;
return 1;
}
int main() {
ifstream infile("../other_code/1.in");
TextQuery file(infile);
vector<array<std::string,3>> h;
while (1) {
std::string sought1,sought2,sought3;
if (!get_word(sought1)) break;
if (sought1 != "h") {
cout << "\nenter second and third word: ";
cin >> sought2 >> sought3;
Query q = Query(sought1) & Query(sought2) | Query(sought3);
h.push_back({sought1,sought2,sought3});
cout << "\nExecuting Query for: " << q << endl;
const auto results = q.eval(file);
print(cout,results);
} else {
cout << "\nenter Query no,: ";
int i; cin >> i;
if (i < 1 || i > h.size())
cout << "\nBad Query no." << endl;
else {
Query q = Query(h[i-1][0]) & Query(h[i-1][1]) | Query(h[i-1][2]);
cout << "\nExecuting Query for: " << q << endl;
const auto results = q.eval(file);
print(cout,results);
}
}
}
return 0;
}