《C++ Primer》第17章 17.1节习题答案

《C++ Primer》第17章 标准库特殊设施

导读本章介绍了tuple、bitset、正则表达式、随机数和特殊的IO操作。

本章的练习帮助读者熟悉这些标准库设施的使用。

17.1节 tuple类型 习题答案

练习17.1:定义一个保存三个int值的tuple,并将其成员分别初始化为10、20和30。

【出题思路】

本题练习定义tuple。

【解答】

注意只能直接初始化。

tuple<int, int, int> ti{10, 20, 30}

练习17.2:定义一个tuple,保存一个string、一个vector<string>和一个pair<string, int>。

【出题思路】

本题练习定义tuple。

【解答】

tuple<string, vector<string>, pair<string, int>> t;

练习17.3:重写12.3节(第430页)中的TextQuery程序,使用tuple代替QueryResult类。你认为哪种设计更好?为什么?

【出题思路】

在较大的例子中练习定义和使用tuple。

【解答】

首先修改TextQuery的定义:

1.不再包含QueryResult.h,而是包含tuple头文件。

2.将line_no、line_it和print的声明从QueryResult.h拷贝到t_TextQuery.h中。

3.将QueryResult定义为一个tuple类型而不再是一个类,tuple的三项分别是原QueryResult类的数据成员。

#include <tuple>

typedef std::vector<std::string>::size_type line_no;
typedef std::set<line_no>::const_iterator line_it;
typedef std::tuple<std::string, std::shared_ptr<std::set<line_no>>,
std::shared_ptr<std::vector<std::string>>> QueryResult;
//这个声明是必需的,查询函数中需返回QueryResult类型
class TextQuery {
public:
    TextQuery(std::ifstream&);
    QueryResult query(const std::string&) const;
    void display_map();//调试辅助函数:打印映射表
private:
    std::shared_ptr<std::vector<std::string>> file;//输入文件
    //将每个单词映身到它出现的行号的集合
    std::map<std::string, std::shared_ptr<std::set<line_no>>> wm;
    //规范文本:删除标点,并转换为小写
    static std::string cleanup_str(const std::string&);
};

std::ostream &print(std::ostream, const QueryResult&);

然后修改TextQuery.cpp:

1.TextQuery::query成员函数虽然返回QueryResult,但其代码其实不必修改,因为这里构造QueryResult使用的是直接初始化方式,改为tuple类型后,初始化语句的形式和原来完全一致。2.主要修改print,只有它直接访问了QueryResult的成员。对QueryResult对象qr,原来通过qr.sought、qr.lines的形式访问检索词和搜索到的行号集合,现在改为get<0>(qr)、get<1>(qr)这样的形式即可。

ostream &print(ostream &os, const QueryResult &qr)
{
    //如果找到了单词,打印出现次数及所有出现的行号
    os << get<0>(qr) << " occurs " << get<1>(qr)->size() << " "
       << make_plural(get<1>(qr)->size(), "time", "s") << endl;
    //打印单词出现的每一行
    for(auto num:*get<1>(qr)) //对set中每个元素
        //不让用户对从0开始的文本行号困惑
        os << "\t(line " << num + 1 << ")"
           << *(get<2>(qr)->begin() + num) << endl;

    return os;
}

可以看到,修改比较简单,而使用tuple的代码也比定义一个类更为简单。若查询结果只是临时使用,比如输出后即丢弃,则使用tuple是一种简单有效的方式。否则,若查询结果还要用来进行其他处理,定义QueryResult类是更好的方式。

练习17.4:编写并测试你自己版本的findBook函数。

【出题思路】

本题练习用tuple返回多个值。

【解答】

参考书中本节内容编写即可,配套网站上有完整代码供对照。编写主程序测试findBook。

int main(int argc, char **argv)
{
    assert(argc > 1);
    //文件中每个元素 保存一个特定书店的销售记录
    vector<vector<Sales_data>> files;
    for(int cnt = 1; cnt != argc; ++cnt)
        files.push_back(build_store(argv[cnt]));
    ifstream in("findbook.in");//要搜索的ISBN号
    reportResults(in, cout, files);
    return 0;
}

它调用build_store来读取命令行参数中指出的书店销售记录文件,存到files中。然后通过reportResults调用findBook来查找指定书目的销售记录。build_store如下:

vector<Sales_data> build_store(const string &s)
{
    Sales_data item;
    vector<Sales_data> ret;
    ifstream is(s);
    while(read(is, item))
        ret.push_back(item);
    sort(ret.begin(), ret.end(), it); //equal_range要求序列排好序
    return ret;
}

注意,为了正确执行findBook,build_store按书目的isbn进行了排序,其中lt比较两个Sales_data的isbn。

练习17.5:重写findBook,令其返回一个pair,包含一个索引和一个迭代器pair。

【出题思路】

本题练习tuple替代解决方案。

【解答】

相对于tuple版,主要修改:

1.matches的类型改为pair,其中第二个成员还是一个pair,保存书目的起止迭代器。

2.findBook中构造返回值的部分,用make_pair构造一个pair,第一个参数与tuple版本一样,第二个参数直接用found。

3.reportResults从findBook返回的matches中提取数据,进行输出的部分。get<0>(store)、get<1>(store)和get<2>(store)改为store.first、store.second.first和store.second.second。

typedef pair<vector<Sales_data>::size_type,
pair<vector<Sales_data>::const_iterator,
vector<Sales_data>::const_iterator>> matches;

ret.push_back(make_pair(it - files.cbegin(), found));
os << "store " << store.first << " sales: "
   << accumulate(store.second.first, store.second.second, Sales_data(s))
   << endl;

练习17.6:重写findBook,不使用tuple或pair。

【出题思路】

本题练习定义类代替tuple方式。

【解答】

首先定义matches类,它有一个size_type成员和两个迭代器成员,构造函数接受一个size_type参数和一个pair参数来初始化3个成员,另外定义3个成员函数分别获取3个数据成员。

class matches{
public:
    matches(vector<Sales_data>::size_type n,
            pair<vector<Sales_data>::const_iterator,
            vector<Sales_data>::const_iterator> f)
        :num(n), first(f.first), last(f.second)
    { }
    vector<Sales_data>::size_type get_num() const
    { return num; }
    vector<Sales_data>::const_iterator get_first() const
    { return first; }
    vector<Sales_data>::const_iterator get_last() const
    { return last; }
private:
    vector<Sales_data>::size_type num;
    vector<Sales_data>::const_iterator first, last;
};

然后修改reportResults中访问3个数据的方法:

os << "store " << store.get_num() << " sales: "
   << accumulate(store.get_first(), store.get_last(), Sales_data(s))
   << endl;

练习17.7:解释你更倾向于哪个版本的findBook,为什么。

【出题思路】

理解pair、tuple和类实现的差别。

【解答】

对于本题,只是简单使用搜索结果,pair和tuple都是简单直接的实现方式。若搜索结果还需进行复杂的计算、处理,定义一个类对其进行封装更好。

练习17.8:在本节最后一段代码中,如果我们将Sales_data()作为第三个参数传递给accumulate,会发生什么?

【出题思路】

复习Sales_data的构造函数的使用。

【解答】

Sales_data()是Sales_data的默认构造函数,对所有数据成员都采用值初始化,因此isbn被初始化为空字符串。因此,在输出结果中,将看不到书目的isbn。

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值