c++ primer 的 textquery 例子。

  c++ primer 的 textquery 例子,做了好几天。发现对入门c++基础是个很大检测,不像初看时,那么简单。

  起码包含了几个知识点,智能指针,值类型智能指针,树的遍历(递归),构造和析构,多态,操作符重载。

  贴出代码和一些自己体会。

  共有2章完善这个 程序。前一个程序很简单,没什么好说。就是查询文本中,某个单词出现的行号。

  后面要求提出组合查询,几个单词同时存在,或再排除某个单数 出现的行号,的逻辑组合 。

  自己做了很多遍,用了好几种方法,改来改去,最终发现自己思考的过程其实就是书要讲解的思路。

  发现后面发现有几个特别要注意到的地方。

1)书中有个没有特别提出的需求,完成or ,and ,not 组合查询,并当写出一个查询组合,可以方便的运用于任何一个文本数据源。这个要求才会让人必须往多态上想。不然完全不必要用基类和继承处理。

2)本来可以按常规思路,每次计算一个结果,用操作符处理每次结果。现在就要求我们把计算方法延后执行,把执行计算的方法,改为储存方法和此方法所需的数据,也就是储存类的实例。再在需要的时候按照顺序执行方法。

3)编译器根据符号优先级(c++的文法句子),对方法的执行是一个树的遍历过程。所以我们最后我们得到的结果是一个树的根。如,Query tmp=~(Query(star)|Query(little)&Query(Twinkle)); tmp 是树的根节点。

4)用后续遍历法执行每个节点的eval方法。来模拟 编译器对表达式的计算思路。但仔细看的话,会知道方法的执行和当初节点的建立顺序其实不是一样的。不过不妨碍结果。

5)注意二叉树和单子树节点,构造函数的参数是值传递。Node_2L(Query _lhs, Query _rhs,const string & _opstr);

  这样构造的时候,会对lhs,rhs进行直copy。直copy会引发lhs,rhs中,自己的lhs,rhs直copy。一层一层。如此引用型计数器use,才能正常得到引用次数。临时对象析构时,才不会delete handle class,所指向的对象。

6)display的过程是一个树中序遍历的过程。

 完整下载,ide:code block.

部分代码,方便快速查看。

textquery.h

#ifndef TEXTQUERY_H_INCLUDED
#define TEXTQUERY_H_INCLUDED

#include "head.h"

typedef vector<string>::size_type line_no;
typedef map<string,set<line_no> >::iterator Type_mIt;
typedef set<line_no>::iterator Type_sIt;

class TextQuery
{
public:
    TextQuery(){}

    void readfile(ifstream &is)
    {
        store_file(is);
        build_map();
    }

    set<line_no> run_query(const string& word) const
    {
        return word_map.find(word)->second;
    }

    string text_line(line_no line_n) const
    {
        if(line_n<=lines_of_text.size())
        {
            return lines_of_text[line_n];
        }
        else
        {
            throw string("out of range!");
        }
    }

    map<string,set<line_no> >& getmap()
    {
        return word_map;
    }
    int getsumLine()const
    {
        return lines_of_text.size();
    }

    set<line_no> getAllset()const
    {
        set<line_no> tmp;
        for(line_no i=0;i!=lines_of_text.size();++i)
        {
            tmp.insert(i);
        }
        return tmp;
    }

    ~TextQuery()
    {

    }

private:
//data
    vector<string> lines_of_text;
    map<string,set<line_no> > word_map;

//text
    void store_file(ifstream& is)
    {
        string textLine;
        if (!is.is_open())
        {cout << "Error opening file";}
        while (getline(is,textLine))
        {
            lines_of_text.push_back(textLine);
        }
    }

    void build_map()
    {
        for(line_no i=0;i!= lines_of_text.size();i++)
        {
            istringstream words(lines_of_text[i]);
            string word;
            while(words >> word)
            {
                word_map[word].insert(i);
                word="";
            }
        }
    }
};

#endif // TEXTQUERY_H_INCLUDED

head.h

#ifndef HEAD_H_INCLUDED
#define HEAD_H_INCLUDED
//标准库--》类型缩写--》类定义--》cpp文档,函数申明--》 通用函数。--》其他函数。

#include <set>
#include <string>
#include <vector>
#include <map>
#include <set>
#include <iostream>
#include <fstream>
#include <cctype>
#include <cstring>
#include <sstream>
#include <algorithm>
using namespace std;

typedef vector<string>::size_type line_no;

//main.cpp
void linkqureyf2();
void linkqureyf();
void simQuery();
void textquery();
void opqueryF();
void outLine(ostream& of,const string &content);
void print_result(const map<string,set<line_no> >& wordsLine);
void print_result2(const set<line_no> &wordsLine);
void treefun();

//simple
set<line_no> OrSet(const set<line_no>& ls,const set<line_no>& rs);
set<line_no> AndSet(const set<line_no>& ls,const set<line_no>& rs);
set<line_no> NotSet(const set<line_no>& sourceSet,const set<line_no>& AllSet);

#endif // HEAD_H_INCLUDED

main.cpp

#include "head.h"
#include "textquery.h"
#include "simplequery.h"
#include "opquery.h"
#include "LinkQuery.h"
#include "lineLinkQuery.h"
#include "TreeQuery.h"

vector<u_p> LinkQuery::funs=vector<u_p>();
bool Query::debug=false;
int main()
{
    //textquery();
    //simQuery();
    //opqueryF();
    //linkqureyf();
    treefun();
    return 0;
}
//依据表达式用最恰当的树的数据结构,来储存实例。建立树。
//采用后序遍历法,访问每个节点,执行方法,来达到表达式同样的结果(尽管顺序其实不是严格一致)。

void treefun()
{
    ifstream infile("littlestar.txt");
    TextQuery file;
    file.readfile(infile);

    outLine(cout,"words-lineNo mapping.....................");
    print_result(file.getmap());
    string little="little";
    string Twinkle="Twinkle";
    string star="star";

    Query tmp=~(Query(star)|Query(little)&Query(Twinkle));

    cout<<tmp.NodeName()<<endl;
    cout<<tmp.display(cout)<<endl;
    print_result2(tmp.Eval(file));

}
void textquery()
{
    ifstream infile("littlestar.txt");
    TextQuery file;
    file.readfile(infile);

    outLine(cout,"words-lineNo mapping.....................");
    print_result(file.getmap());
    set<line_no> ret= file.run_query("little");
    outLine(cout,"‘little line-no:‘.....................");
    print_result2(ret);
}

void outLine(ostream& of,const string &content)
{
    of<<"***********"<<content<<"***********"<<endl;
}

void print_result(const map<string,set<line_no> >& wordsLine)
{
    map<string,set<line_no> >::const_iterator beg1;
    for(beg1=wordsLine.begin();beg1!=wordsLine.end();beg1++)
    {
        cout<<beg1->first<<":";
        set<line_no>::iterator bega=beg1->second.begin();
        for(line_no i=0;i<beg1->second.size();i++,bega++)
        {
            cout<<*bega+1<<",";
        }
        cout<<endl;
    }
}

void print_result2(const set<line_no> &wordsLine)
{
    set<vector<string>::size_type>::iterator beg=wordsLine.begin();
    while(beg!=wordsLine.end())
    {
        cout<<*beg+1<<",";
        ++beg;
    }
    cout<<endl;
}

treequery.h

#ifndef TREEQUERY_H_INCLUDED
#define TREEQUERY_H_INCLUDED
#include "head.h"
#include "textquery.h"

//base class
//所有方法,数据都private,让handle为友类访问,操作符重载方法也访问。
//注意看构造函数,//所以const 和&,需要认真理解,完成例子后,再用时间熟悉下const。
class Query;
ostream& operator<<(ostream &os, const Query& q);
Query operator&(const Query& lhs,const Query& rhs);
Query operator|(const Query& lhs,const Query& rhs);
Query operator~(const Query& lhs);

class base_TNode
{
protected:
    virtual ~base_TNode();
private://方法全部隐藏,且虚函数。由handle class来代理,并根据实际对象,调用自己的。
    virtual set<line_no> Eval(const TextQuery& _file)=0;
    virtual string NodeName()const=0;//因为此方法会被派生类的数据成员lhsrhs调用。 而Node_2L,和notnode中的数据成员lhs,rhs,都为const。所以这里要const。
    virtual ostream& display(ostream& os)const=0;//同上。

friend class Query;
friend Query operator&(const Query& lhs,const Query& rhs);
friend Query operator|(const Query& lhs,const Query& rhs);
friend Query operator~(const Query& lhs);
};

base_TNode::~base_TNode(){}

//handle class,有base_TNode,基类的指针成员。
//作为功能之一:智能指针,所有使用基类指针的地方,必须使用Query。以便计数器正确。
//作为功能之二,handle。handle class 必须代理派生类方法。
class Query
{
public:

    Query(const Query& up);
    Query& operator=(const Query& up);
    ~Query();

    Query(const string& _word);
    set<line_no> Eval(const TextQuery& _file)const;
    string NodeName() const;//连派生类访问基类的方法。都要通过handle class。
    ostream& display(ostream& os)const;

    //ostream & operator<<(const Query& _qy);

    static bool debug;

private:
    Query(base_TNode* _p);//仅仅给操作符重载使用,隐藏,把操作符重载方法,加为友元。
    base_TNode* p;//必须隐藏指针,基类的方法都由handle class。代理。
    unsigned int * use;
    void del();

friend  Query operator|(const Query& lhs,const Query& rhs);
friend  Query operator&(const Query& lhs,const Query& rhs);
friend  Query operator~(const Query& lhs);
};

//leaf
class Leaf:public base_TNode
{
private:
    Leaf(const string& _word);//隐藏派生类,只给handle class 友类来访问。
    string NodeName()const;
    string word;
    set<line_no> Eval(const TextQuery&);
    ostream& display(ostream& os)const;
friend class Query;
};
//friend Query::Query(base_TNode* _p);
Leaf::Leaf(const string& _word):word(_word){}
string Leaf::NodeName()const
{
    return word;
}
set<line_no> Leaf::Eval(const TextQuery& _file)
{
    //if(no left child and no right child)
    //看成后序遍历的,递归终结的临界点。
    return _file.run_query(word);
}

ostream& Leaf::display(ostream& os)const
{
    return os<<word;
}

//base of 2-child node
class Node_2L:public base_TNode
{
protected:
    Node_2L(Query _lhs, Query _rhs,const string & _opstr);//参数值传递Query,(handle class) 对象。这样构造的时候,会对lhs,rhs进行直copy。
    //直copy会引发lhs,rhs中,自己的lhs,rhs直copy。一层一层。如此引用型计数器use,才能正常得到引用次数。临时对象析构时,才不会delete handle class,所指向的对象。
    const Query  lhs;
    const Query  rhs;
    ostream& display(ostream& os)const;
private:
    string NodeName()const;

    string opstr;
};
Node_2L::Node_2L(Query _lhs, Query _rhs,const string & _opstr):lhs(_lhs),rhs(_rhs),opstr(_opstr){}
string Node_2L::NodeName()const
{
    return "("+lhs.NodeName()+opstr+rhs.NodeName()+")";
}

ostream& Node_2L::display(ostream& os)const
{
    //按照简单的下面方式得不到正确结果,会把lhs.display(os)当成一个地址打出来。
    //用简单方法,更容易维护。比如用NodeName方法同样的效果。那就必须重新解释<<的含义。重载<<。
    //return os<<"("<<lhs.display(os)<<opstr<<rhs.display(os)<<")";
     return os << "(" << lhs  << " " << opstr << " "<< rhs << ")";
}

//and node
class AndNode:public Node_2L
{
private:
    AndNode( Query _lhs, Query _rhs);
    set<line_no> Eval(const TextQuery& _file);

friend  Query operator&(const Query& lhs,const Query& rhs);//操作符重载需要访问。
};
AndNode::AndNode(Query _lhs, Query _rhs):Node_2L(_lhs,_rhs,"&"){}

set<line_no> AndNode::Eval(const TextQuery& _file)
{
    set<line_no> leftret=lhs.Eval(_file);
    set<line_no> rightret=rhs.Eval(_file);
    return AndSet(leftret,rightret);
}

//or node
class OrNode:public Node_2L
{
private:
    OrNode( Query _lhs, Query _rhs);
    set<line_no> Eval(const TextQuery& _file);

friend  Query operator|(const Query& lhs,const Query& rhs);//操作符重载需要访问。
};
OrNode::OrNode( Query _lhs, Query _rhs):Node_2L(_lhs,_rhs,"|"){}

set<line_no> OrNode::Eval(const TextQuery& _file)
{
    set<line_no> leftret=lhs.Eval(_file);
    set<line_no> rightret=rhs.Eval(_file);
    return OrSet(leftret,rightret);
}

//not node
class NotNode:public base_TNode
{
private:
    NotNode(Query lhs);
    set<line_no> Eval(const TextQuery& _file);
    ostream& display(ostream& os)const;
    string NodeName()const;
    const Query lhs;
friend  Query operator~(const Query& lhs);
};

NotNode::NotNode(Query _lhs):lhs(_lhs){}
string NotNode::NodeName()const
{
    return "~("+lhs.NodeName()+")";
}

set<line_no> NotNode::Eval(const TextQuery& _file)
{
    set<line_no> leftret=lhs.Eval(_file);
    return NotSet(leftret,_file.getAllset());
}

ostream& NotNode::display(ostream& os)const
{
    ////???????????????????????
    return os << "~(" << lhs << ")";
}

//定义
Query::Query(base_TNode * _p):p(_p),use(new unsigned int(1)){if(Query::debug){cout<<p->NodeName()<<":  ‘*p‘ constr to query:"<<p<<"     use:."<<*use<<endl;}}

Query::Query(const Query& up)
{
    p=up.p;
    use=up.use;
    ++*use;
    if(Query::debug){cout<<p->NodeName()<<": ref:"<<p<<"     use:."<<*use<<endl;}
}

Query& Query::operator=(const Query& up)
{
    ++*up.use;
    del();
    p=up.p;
    use=up.use;
    if(Query::debug){cout<<p->NodeName()<<": copy =:"<<p<<"     use:."<<*use<<endl;}
    return *this;
}
Query::~Query()
{
    string tmp=p->NodeName();
    if(Query::debug){cout<<tmp<<": before delete1  :"<<p<<"     use:."<<*use<<endl;}
    del();
}

void Query::del()
{
    string tmp=p->NodeName();
    if(Query::debug){cout<<tmp<<": before delete2  :"<<p<<"     use:."<<*use<<endl;}
    if(--*use==0)
    {
        delete p;
        delete use;
        if(Query::debug){cout<<tmp<<": delete end"<<endl;}
    }
    else
    {
        if(Query::debug){cout<<tmp<<": delete end(just sub use.)  :"<<p<<"     use:."<<*use<<endl;}
    }
}

string Query::NodeName()const
{
    return p->NodeName();
}

Query::Query(const string& _word)
{
    p=new Leaf(_word);
    use=new unsigned int(1);
    if(Query::debug){cout<<p->NodeName()<<": query ‘word‘ construct "<<p<<"     use:."<<*use<<endl;}
}

set<line_no> Query::Eval(const TextQuery& _file)const
{
    return p->Eval(_file);
}

ostream& Query::display(ostream& os)const
{
    return p->display(os);
}

//end query 定义

Query operator&(const Query& lhs,const Query& rhs)
{
//    base_TNode *p=new AndNode(lhs,rhs);
//    if(Query::debug){cout<<p->NodeName()<<": pointer create "<<p<<endl;}
//    Query tmp=Query(p);
//
//    return tmp;
    return Query(new AndNode(lhs,rhs));//raii 原则,获得资源即初始。
}

Query operator|(const Query& lhs,const Query& rhs)
{
//    base_TNode *p=new OrNode(lhs,rhs);
//    if(Query::debug){cout<<p->NodeName()<<": pointer create "<<p<<endl;}
//    Query tmp=Query(p);
//
//    return tmp;
    return Query(new OrNode(lhs,rhs));//raii 原则,获得资源即初始。
}

Query operator~(const Query& lhs)
{
//    base_TNode *p=new NotNode(lhs);
//    if(Query::debug){cout<<p->NodeName()<<": pointer create "<<p<<endl;}
//    Query tmp=Query(p);
//
//    return tmp;
    return Query(new NotNode(lhs));//raii 原则,获得资源即初始。
}

inline ostream& operator<<(ostream &os, const Query& q)
{
    return q.display(os);
}

#endif // TREEQUERY_H_INCLUDED
时间: 2024-10-20 20:31:36

c++ primer 的 textquery 例子。的相关文章

0722-----C++Primer听课笔记----------句柄类和智能指针

1.再说智能指针 1.1  为什么要用智能指针?对于一个指针,它指向一个动态分配内存的对象,若同时有多个指针指向该对象,那么当我们delete的时候,就会出现delete 一个无效内存的错误,因为该对象已经被delete过了,所以这就造成了错误.针对这一情况,我们想到,new 和 delete 必须是成对出现的,那么联想到类里面,很容易想到这个构造函数和析构函数也是成对出现的,对于每一个对象,初始化的时候会调用构造函数,而销毁的时候必然要调用析构函数.因此我们就可以对 指针 进行封装,将该指针的

C++ 11 vlearning

1.新增算术类型     longlong,最小不比long小,一般为64位. 2.列表初始化      int units_sold = {0};或者 int units_sold{0};非11标准下的C++中,只有特定的情况下才能使用该形式.  比如数组的初始化,类构造函数的初始化,结构体的填充.相比传统的赋值初始化,如果右侧的数值类型相对于左侧类型更大的话,侧对于这种窄化现象,编译器会 报错.如:int k = {3.14};一个double是8个字节,int一般是4个字节,这时编译器就会

条件操作符(三元操作符)

C++中有一个需要注意的操作符,这个操作符石C++中唯一的三元操作符,这个操作符就是条件操作符. 用法: cond ? value1 : value2; 简介: 这个表达式的求解顺序是计算cond的值是否等于0,如果等于0(也就是false),那么返回value2(如果value2是一个表达式,那么计算表达式的值并返回计算结果),否则返回value1(如果value2是一个表达式,那么计算表达式的值并返回计算结果). 使用三元操作符的时候需要注意以下几点: (1)避免三元操作符的深度嵌套.如下,

《C++ Primer》 chapter 15 TextQuery

<C++ Primer>中第15章为了讲解面向对象编程,举了一个例子:设计一个小程序,能够处理查询给定word在文件中所在行的任务,并且能够处理“非”查询,“或”查询,“与”查询.例如执行查询 one & of |the ,表示对单词one和of的查询结果取交集,然后对单词the的查询结果取并集. 书中查询的底层操作实际定义在类TextQuery中,我们在TextQuery的基础上,进一步封装并实现如下图所示的类结构,能够达到上述功能需求.类之间的结构如下图所示: 程序扼要设计如下表所

c++ primer,友元函数上的一个例子(By Sybase)

本文试图解释c++ primer Screen 和 Window_Mgr的例子,为什么将两个类放在两个文件中无法编译? 将两个类写在同一个文件中,通过三个例子解释问题: 第一种写法问题: 编译到Screen时,由于Screen类使用到Window_Mgr的成员函数,虽然前面给出了Window_Mgr的声明,但此时还清楚Window_Mgr的完整定义,所以编译出错. class Window_Mgr class Screen { public: friend Window_Mgr& Window_

C Primer Plus 例子6.5中的小问题

程序清单6.5 compflt.c是比较浮点数是否相等的例子. 原程序如下: // cmpflt.c -- 浮点数比较 #include <math.h> #include <stdio.h> int main(void) { const double ANSWER = 3.14159; double response; printf("What is the value of pi?\n"); scanf("%lf", &respo

dogs.cpp_程序清单1.1_C源代码的例子(《C Primer Plus》_P17)

// dogs.cpp : 定义控制台应用程序的入口点. // /* 时间:2018年05月29日 20:04:07 目的:printf scanf 基本用法 代码练习:<C Primer Plus>P17 程序清单1.1 C源代码的例子 */ #include "stdafx.h" // 在vs2010 的头文件 ,旧版: # include <stdio.h> //括号里面分别定义了一个整数型的参数个数(int argc )和一个char类型的指针表示参数的

c++ primer第15章这几个例子中的构造函数形式不太理解

//向基类构造函数传递实参p491 class Bulk_item : public Item_base{ public: Bulk_item(const std::string& book,double sales_price,std::size_t qty = 0,double disc_rate = 0.0): Item_base(book,sales_price),min_qty(qty),discount(disc_rate){} }; //在派生类构造函数中使用默认实参p491 cl

C++ Primer 学习笔记_83_模板与泛型编程 --一个泛型句柄类

模板与泛型编程 --一个泛型句柄类 引言: [小心地雷] 这个例子体现了C++相当复杂的语言应用,理解它需要很好地理解继承和模板.在熟悉了这些特性之后再研究这个例子也许会帮助.另一方面,这个例子还能很好地测试你对这些特性的理解程度. 前面示例的Sales_item和Query两个类的使用计数的实现是相同的.这类问题非常适合于泛型编程:可以定义类模板管理指针和进行使用计数.原本不相关的Sales_item类型和 Query类型,可通过使用该模板进行公共的使用计数工作而得以简化.至于是公开还是隐藏下