日本免费高清视频-国产福利视频导航-黄色在线播放国产-天天操天天操天天操天天操|www.shdianci.com

學無先后,達者為師

網站首頁 編程語言 正文

C++詳解Primer文本查詢程序的實現_C 語言

作者:配的上了嗎 ? 更新時間: 2022-08-17 編程語言

15.9的文本查詢程序是對12.3節的文本查詢程序的擴展,而使用的主要知識也是15章的核心:繼承和多態,即面向對象程序設計。

恩,這一節看的過程中,會有很多不理解。特別是在沒有把整個程序都看完之前,會有很多疑惑,而看完之后,再思考思考,回頭再看本節的前面所寫的程序介紹,會有一些感悟。更加清楚這個程序的原理。

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

出現了一些意外,代碼中中文注釋都是亂碼。

針對程序所涉及的幾個類的介紹和理解:

TextQuery類:

可以把每個TextQuery類對象看作一個文本文件,這個類將某個文本文件的內容保存在一個vector<string>中,并保存了每個單詞對應的行號,而query函數就是接收一個string,然后查找這個單詞。而這里的返回結果是一個QueryResult類對象。這個類只是用來保存一個查詢結果,其實后續的& | ~的結果也都是這個QueryResult類對象

在12章時,我想過,為什么要設計這么一個類呢?如果直接在query函數中實現查找并打印不可以嗎?其實這樣是不太合適的,一個最直接的原因就是,在后方進行word1 & word2操作時,不方便,封裝一個查詢結果類更容易處理。這樣,也可以支持更多的操作,而不僅僅是打印。

QueryResult類:

表示一個查詢結果,通常與print函數聯系起來使用,print用于打印這個查詢結果。

后續的就是一些新的繼承方面的類了,也就是為了支持word1 & word2 或者 word1 | word2 或者 ~word操作。而這些查詢都建模成了相互獨立的類,即AndQuery OrQuery NotQuery 而最基本的還有一個WordQuery,這些類都繼承自一個抽象基類Query_base。

Query_base類:

最主要的就是兩個成員函數:eval 和 rep,說真的,我覺得這兩個名字起的并不好,當然受限于我的英文水平,其實eval就相當于TextQuery類的query函數,參數是TextQuery,即一個文本文件,然后在這個文本文件中執行查詢操作,返回一個查詢結果QueryResult。rep函數用于返回查詢的string表示形式,比如~(word1 & word2)。

Query類:

這個類是很重要的,當然這句話是句廢話.... 這個類的數據成員是一個基類的指針,而這也是這個程序支持面向對象編程和多態的根本原因。

這是一個接口類,它的成員函數仍然是eval和rep,調用的是基類指針所指向對象的eval和rep,基類指針或引用調用虛函數發生動態綁定。所以,Query類基類指針指向的對象,可能是繼承體系中任何一種類型的對象。比如: Query q = Query("dog") & Query("cat"); 而這里的q的基類指針指向的就是一個AndQuery類的對象,調用的eval和rep也都是AndQuery類版本的eval和rep,而這個AndQuery類的數據成員就包括著右邊&運算符左右兩邊的兩個WordQuery類的對象,這里是使用了&運算符重載。operator& 返回的就是一個基類指針綁定到AndQuery類對象的Query類對象。返回值用于初始化q。這里調用的應該是Query類的拷貝構造函數吧

WordQuery類:

Query_base類的派生類,表示對于某個單詞最直接的查詢,覆蓋了eval和rep,為什么說eval相當于query呢?這里的eval就是最明顯的證明:這里的eval直接返回參數TextQuery類的query結果,就是對某個單詞的查詢結果。而后方的Not And Or,都沒有調用這個query操作,他們操作的是Query類對象的查詢結果。

NotQuery類:

這個類也是Query_base的派生類,表示~查詢方式。~運算符重載之后,返回的就是一個綁定到NotQuery類對象上的Query類對象,而~作用的就是另一個Query類對象的eval查詢結果。

AndQuery OrQuery類:

因為這兩個類都操作兩個Query類對象,所以又實現了一個BinaryQuery抽象基類,這個基類繼承自Query_base,多了兩個Query類對象的成員,以及一個操作符成員,用于表示& 還是 |。

這兩個類所關聯的是& |運算符,operator& 返回的分別是是基類指針綁定到AndQuery類對象上的Query類對象 。operator | 返回的是基類指針綁定到OrQuery類對象上的Query類對象,而這兩個類的rep函數很簡單,對于兩個成員的rep函數進行一些簡單加工即可,而eval函數,參數仍然是一個TextQuery類,在兩個Query成員返回的QueryResult上進行處理,然后返回一個新的QueryResult對象。代表著一種& 或者 |操作之后的查詢結果。

還有一個比較有趣的是:

如下代碼:

Query q = Query("Dog") & Query("Cat") | Query("Bird");

Print( cout, q.eval(textquery) );

一共創建了三個WordQuery,一個AndQuery,一個OrQuery。先后順序不太清楚。。。但是其實創建好q對象之后,這里面并沒有什么查詢結果,保存的只是這些單詞,還有一些沒有調用的成員函數eval和rep。

根據運算符優先級規則,q是一個基類指針指向OrQuery類對象的Query對象,而如果想打印出這個查詢結果,必然是要調用eval函數的,參數表示,在這個文件里查找這三個單詞。在OrQuery的eval調用的最開始,兩個Query類對象數據成員的查詢結果還沒有出來,而在eval函數內部,計算了兩個查詢結果,一個是rhs數據成員的對Bird單詞的查詢,查詢的位置就是那個textquery保存的文件內容。另一個是AndQuery的eval函數的返回結果,這個結果是對兩個WordQuery類對象查詢結果的&操作之后的結果。最后才對這兩個QueryResult結果進行合并處理。然后返回一個新的查詢結果。

現在看來,只有WordQuery類對象調用了TextQuery的query操作。而其余的Or Not And都是對其他的Query對象的查詢結果進行加工。當然這些eval函數的參數都是同一個TextQuery。并且都是返回的QueryResult。

說真的,之前比較疑惑的是,我感覺這些eval函數的TextQuery參數的傳遞有些奇怪。對比之前12章的文本查詢程序,最后封裝的對文件的查詢的函數,看上去就舒服多了,它是把ifstream類對象傳遞給TextQuery類的構造函數的參數,然后后面調用query函數進行查詢,返回一個QueryResult對象。調用print函數打印。

但是這里再探的程序就有點不一樣和奇怪了,你也可以封裝一個完整的查詢函數,但是如果不那樣做的話,進行的操作就是。

Query q = Query("Dog") & Query("Cat") | Query("Bird");

Print( cout, q.eval(textquery) );

這種操作,相比于 TextQuery tq(ifile); print(cout, tq.query("Dog")); 就有點奇怪了。

上方的main.cpp主函數,并沒有實現完善的查詢函數,即實時查詢操作,如輸入Dog & Cat | Bird。然后打印查詢結果,之后有能力實現的話可能會補上。

就以這篇文章作為大一學習生活的結束吧。

原文鏈接:https://blog.csdn.net/i777777777777777/article/details/125343846

欄目分類
最近更新