文本查询程序(类的继承体系)

  • 单词查询的基础上实现3个逻辑查询
  1. ~得到所有不匹配的行
  2. &将2个匹配结果取交集
  3. |将2个匹配结果取并集
  • 表达式查询:Query("xx") & Query("xx") | Query("xx")

    如何抽象出基类 且 定义接口类

  1. TextQuery已经实现了单词查询,如果剩下的3个操作通过TextQuery派生.但是当2个对象进行&和|操作时,我们又要定义 派生类结果的&(交集)和|(并集)的函数
  2. 将不同的操作定义成不同的类,共享一个基类,并且使用基类的eval()函数这个公共接口.
    1. WordQuery
    2. NotQuery
    3. AndQuery
    4. OrQuery
  • 建立4个类封装各自的eval()函数得到结果,接收TextQuery,返回QueryResult,
  1. WordQuery:单词查询,实际调用的是TextQuery::query()
  2. NotQuery:一个运算对象,
  3. AndQuery:2个运算对象,在WordQuery基础上交结果
  4. OrQuery:2个运算对象,在WordQuery基础上并结果
  • 由于AndQueryOrQuery有2个运算对象,那么不妨抽象出一个基类BinaryQuery将2个对象保存在此类中.
  • 4个对象都具有eval函数,创建一个抽象基类(Query_base)来表示该接口,并且将eval定义成虚函数,所有特殊查询类需要重写该函数.通过一个抽象基类包含一个基类指针来存子类对象实现对eval函数的虚调用
  • 但是该基类只作为eval函数的接口,而我们只想通过一个类来完成所有操作,那么将Query作为一个接口类,将eval这个函数接口封装一下

    并且接口类通过Query_base指针绑定到Query_base的不同派生类对象上

  1. &Query对象绑定到AndQuery对象上
  2. |,~ 同理
  • 通过接口类能够实现支持不同的运算符和派生类对象的转换

体会Query的作用,

test.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
#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>>还实现了对历史操作的查询功能
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    #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;
    }


本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!