BinaryQuery, Query, WordQuery, Query, BinaryQuery, Query, WordQuery,
Query, WordQuery, BinaryQuery, Query, WordQuery, Query, WordQuery,
BinaryQuery, Query, WordQuery, Query, BinaryQuery, Query, WordQuery,
Query, WordQuery, Query, BinaryQuery, Query, WordQuery, Query, BinaryQuery,
Query, WordQuery, Query, WordQuery
(c)计算q.eval时所调用的eval函数如下:
Query类的eval,OrQuery类的eval,AndQuery类的eval,WordQuery类的eval
练习15.35:实现Query类和Query_base类,其中需要定义rep而无须定义eval。
【出题思路】
本题是类构造的练习。
【解答】
storyDataFile.txt文件内容如下:
Alice Emma has long flowing red hair.
Her Daddy says when the wind blows
through her hair, it looks almost alive,
like a fiery bird in flight.
A beautiful fiery bird, he tells her,
magical but untamed.
"Daddy, shush, there is no such thing,"
she tells him, at the same time wanting
him to tell her more.
Shyly, she asks, "I mean, Daddy, is there?"
//TextQuery.h
#ifndef TEXTQUERY_H
#define TEXTQUERY_H
#include
#include
#include
#include
//TextQuery.cpp
#include "TextQuery.h"
#include "make_plural.h"
#include
#include
#include
#include
#include
#include
//make_plural.h
#include
using std::size_t;
#include
using std::string;
#include
using std::cout;
using std::endl;
#ifndef MAKE_PLURAL_H
#define MAKE_PLURAL_H
// return the plural version of word if ctr is greater than 1
inline
string make_plural(size_t ctr, const string &word,
const string &ending)
{
return (ctr > 1) ? word + ending : word;
}
#endif
#include
#include
#include
#include
#include
#include
#include
#include
#include //multiset
#include
运行结果:
练习15.36:在构造函数和rep成员中添加打印语句,运行你的代码以检验你对本节第一个练习中(a)、(b)两小题的回答是否正确。
【出题思路】
检验类层次中成员函数的调用关系。
【解答】
在各个类的构造函数和rep中添加打印语句即可。注意,Query类有一个public和一个private共两个构造函数。
练习15.37:如果在派生类中含有shared_ptr类型的成员而非Query类型的成员,则你的类需要做出怎样的改变?
【出题思路】
练习类层次的不同实现方式。
【解答】
书中的实现方式是用Query类封装了Query_base指针,管理实际查询处理用到的不同Query类型对象。
如果不使用Query类,则涉及使用Query类型的地方,都要改成Query_base指针。如创建单个词查询时,就必须创建WordQuery类而不是Query对象。几个重载的布尔运算符也不能再针对Query对象,而需针对Query_base指针,从而复杂的查询请求无法写成目前的简单形式,而需逐个运算完成,将结果赋予Query_base指针,然后再进行下一步运算。资源管理方面也需要重新设计。因此,当前的设计仍是最佳方式。
练习15.38:下面的声明合法吗?如果不合法,请解释原因;如果合法,请指出该声明的含义。
BinaryQuery a = Query("fiery") & Query("bird");
AndQuery b = Query("fiery") & Query("bird");
OrQuery c = Query("flery") & Query("bird")
【出题思路】
理解虚函数和类层次的概念。
【解答】
第一条声明不合法,因为BinaryQuery中的eval是纯虚函数。
第二条声明不合法,不能将Query转换为AndQuery。
第三条声明不合法,不能将Query转换为OrQuery。
练习15.39:实现Query类和Query_base类,求图15.3(第565页)中表达式的值并打印相关信息,验证你的程序是否正确。
【出题思路】
练习复杂类层次的实现。
【解答】
参考书中本节内容和配套网站中的代码即可。
练习15.40:在OrQuery的eval函数中,如果rhs成员返回的是空集将发生什么?如果lhs是空集呢?如果lhs和rhs都是空集又将发生什么?
【出题思路】
理解并集的计算过程。
【解答】
OrQuery的eval从lhs和rhs获取范围来构造set(或向其插入),而set的构造和插入操作可以正确处理空范围,因此无论lhs和rhs的结果是否为空集,eval都能得到正确结果。
练习15.41:重新实现你的类,这次使用指向Query_base的内置指针而非shared_ptr。请注意,做出上述改动后你的类将不能再使用合成的拷贝控制成员。
【出题思路】
练习在复杂程序中自己实现资源管理。
【解答】
关键是在没有了shared_ptr的帮助后,要为Query类设计拷贝控制成员,管理内存。
除了将成员q的类型改为Query_base*外,还需增加引用计数成员int* uc,并增加拷贝构造函数、拷贝赋值运算符和析构函数。具体方法参考第13章的练习即可。当然,其他用到q的地方也需要进行修改。
#include
#include
#include
#include
#include
#include
#include
#include
#include //multiset
#include
运行结果:
练习15.42:从下面的几种改进中选择一种,设计并实现它:(a)按句子查询并打印单词,而不再是按行打印。(b)引入一个历史系统,用户可以按编号查阅之前的某个查询,并可以在其中增加内容或者将其与其他查询组合。(c)允许用户对结果做出限制,比如从给定范围的行中挑出匹配的进行显示。
【出题思路】
本题练习复杂程序的设计和实现。
【解答】
在配套网站中的代码基础上进行如下修改:(a)要支持基于同一句子而不是同一行计算单词,只需将本文按句子而不是按文本行存储到vector容器。在TextQuery.cpp中将TextQuery类的构造函数修改如下:
//读输入文件,将每个句子存储为lines_of_text的一个元素
TextQuery::TextQuery(ifstream &is)
:file(new vector)
{
char ws[] = {'t', 'r', 'v', 'f', 'n'};
char eos[] = {'?', '.', ',', '!'};
set whiteSpace(ws, ws + 5);//空白符
set endOfSentence(eos, eos + 3);//句子结束符
string sentence;
char ch;
while(is.get(ch))
{
//未遇到文件结束符
if(!whiteSpace.count(ch)) //非空白符
{
sentence += ch;
}
//读完一个句子
if(endOfSentence.count(ch))
{
file->push_back(sentence);
int n = file->size() - 1;
istringstream is(sentence);
string word;
while(is >> word)
{
auto &lines = wm[word];
if(!lines)
{
lines.reset(new set);
}
lines->insert(n);
}
//将sentence清空,准备读下一个句子
sentence.assign("");
}
}
}
此外,将print函数中输出提示的"line"改为"sentence"。
(b)为了记录查询历史,可声明一个vector,每个元素是保存3个string的定长array,保存一个查询的3个查询词。还需修改get_word(s)和主程序的逻辑,允许用户选择历史查询。当用户输入一个合法的查询编号时,从vector对应元素中提取出3个查询词,重构Query,完成查询并输出结果。查询添加内容及多查询组合的功能可类似添加。
bool get_word(string &str)
{
cout << "enter a word to search for, or q to quit, or h to history: ";
if(!(std::cin >> str) || str == "q")
{
std::cout << str;
return false;
}
else
{
std::cout << str;
return true;
}
}
int main(int argc, char **argv)
{
//读取文件建立映射表
//TextQuery file = get_file(argc, argv);
std::ifstream infile;
infile.open("storyDataFile.txt");
TextQuery file = infile;
vector> h;
//程序主循环:提示用户输入一个单词,在文件中查找它并打印结果
while(true)
{
string sought1, sought2, sought3;
if(!get_word(sought1))
{
break;
}
if(sought1 != "h")
{
cout << "nenter second and third words:";
cin >> sought2 >> sought3;
//对给定字符串查找所有出现位置
Query q = Query(sought1) & Query(sought2) | Query(sought3);
h.push_back({sought1, sought2, sought3});
//h.push_back({sought1, sought2, sought3});
cout << "nExecuting Query for: " << q << endl;
const auto results = q.eval(file);
//打印匹配结果
print(cout, results, 0, 10);
}
else //用户输入了"h“,表示要提取历史查询
{
cout << "nenter Query no.:";
int i;
cin >> i;
if(i < 1 || i > h.size()) //历史编号合法性检查
{
cout << "nBad Query no." << endl;
}
else
{
//提取3个查询词,重构查询
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, 0, 10);
}
}
}
return 0;
}
(c)定义另一个版本的print,接受两个参数beg和end,指出要输出的行号即可。
ostream &print(ostream &os, const QueryResult &qr, int beg, int end)
{
//如果找到了单词,打印出出次数及所有出现的行号
os << qr.sought << " occures " << qr.lines->size() << " " << make_plural(qr.lines->size(), "time", "s") << endl;
//打印单词出现的每一行
for(auto num: *qr.lines)//对set中每个元素
{
//不让用户对从0开始的文本行号困惑
if(num + 1 >= beg && num + 1 <= end)
{
os << "t(line " << num + 1 << ") " << *(qr.file->begin() + num) << endl;
}
}
return os;
}