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

  • 单词查询的基础上实现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

#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;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值