C++Primer第10章关联容器习题

习题10.23 编写程序将被排除的单词存储在vector对象中,而不是存储在set对象中。请指出使用set的好处。

使用set的好处是:可以简单地使用count函数来检查单词是否出现在排除集中。(而使用vector则需要循环比较来完成)

*********************************/
#include <iostream>
#include <set>
#include <map>
#include <stdexcept>
#include <fstream>
#include <sstream>
#include <vector>
using namespace std;
void restricted_wc(ifstream &remove_file, map<string, int>&word_count);
int main()
{
    map<string, int> wordCount;
    string fileName;

    //读入包含单词排除集的文件的名字并打开相应文件
    cout << "Enter filename:" << endl;
    cin >> fileName;
    ifstream exFile(fileName.c_str());
    if(!exFile)//打开文件失败
    {
        cout << "error: can not open file:" << fileName << endl;
        return -1;
    }

    restricted_wc(exFile, wordCount);

    //输出map中的单词排除集中单词个数
    cout << "word\t\t" << "times" << endl;
    map<string, int>::iterator iter = wordCount.begin();
    while(iter != wordCount.end())
    {
        cout << iter->first << "\t\t" << iter->second << endl;
        iter++;
    }
    return 0;
}
void restricted_wc(ifstream &remove_file, map<string, int>&word_count)
{
    //set<string> excluded; //set to hold words we'll ignore
    vector<string> excluded;  //保存被排除的单词
    string remove_word;

    while(remove_file >> remove_word)
        //excluded.insert(remove_word); //error?
        excluded.push_back(remove_word);

    cout << "the words in vector:" << endl;
    vector<string>::iterator sit = excluded.begin();
    for(; sit != excluded.end(); ++sit)
        cout << *sit << endl;

    //读入文本并对不在排除集中的单词进行出现次数统计
    string word;
    cout << "enter a word that excluded:(ctrl+z to end)" << endl;
    while(cin >> word)
    {
        bool find = false;

        //确定该单词是否在排除集中
        vector<string>::iterator sit = excluded.begin();
        for(; sit != excluded.end(); ++sit)  //处理的单个字符,而不是字符串
        {
            if(*sit == word)
            {
                find = true;
                break;
            }

        }
        //如果单词不在排除集中,则进行计数
        if(!find)
            ++word_count[word];
    }
}

/***************************************************************************************************

时间:2013-8-12

习题10.24:编写程序通过删除单词尾部的's',生成该单词的非复数版本。
同时,建立一个单词排除集,用以识别以's'结尾,但这个结尾的's'又不能删除的单词。
例如,放在该排除集中的单词可能有success和class。
使用这个排除集编写程序,删除输入单词的复数后缀,而如果输入的是排除集中的单词,则保持该电池不变。

注:怎么删除单词末尾的‘s’,word.resize(word.size() - 1); //去掉单词末尾的‘s’

int main()
{
    set<string> excluded;

    //建立单词排除集
    excluded.insert("success");
    excluded.insert("class");
    //....//可以在此处继续插入其他以s结尾的单词

    string word;
    cout << "enter a word(ctrl+z to end)" << endl;
    //读入单词并根据排除生成单词的非复数版本
    while(cin >> word)
    {
        if(!excluded.count(word)) //该单词未在排除集合中出现
            word.resize(word.size() - 1); //去掉单词末尾的‘s’
        cout << "non-plural vesion:" << word << endl;
        cout << "enter a word(ctrl+z to end)" << endl;
    }

    return 0;
}
习题10.25 

自己写的,很稀烂。

//时间:2013-8-12
//定义一个vector容器,存储你在未来六个月里要阅读的书,再定义一个set,用于记录你已经看过的书。
//编写程序从vector中为你选择一本没有读过而现在要读的书。
//当它位你返回选中的书名后,应该将该书名放入记录已读书目的set中。
//如果实际上你把这本书放在一边没有看,则本程序应该支持从已读书目的set中删除该书的目录。
//在虚拟的六个月后,输出已读书目和没有读的书目。
/*
Q1:怎么查找vector的一个元素?find?
A1:

Q2:怎么在已读书目的set中删除目录,同时添加到vector容器中?
怎么在vector删除未读书目同时把该书目添加到set中?
A1:定义vector容器,里面是pair类型的对象,vector<pair<string,int>>,
其中string代表书目,int标记该书是否已读?

*/
#include <iostream>
#include <vector>
#include <set>
using namespace std;

int main()
{
    vector<string> svec;
    set<string> sset;
    string book_name;
    // bool is_vec = false;

    cout << "enter book name to vector:(ctrl+z to end)" << endl;
    while(cin >> book_name)
        svec.push_back(book_name);

    cin.clear();//added
    cout << "enter a book you want to read:(ctrl+z to end)" << endl;
    while(cin >> book_name)
    {
        //is_vec = false;

        vector<string>::iterator sit = svec.begin();
        while(sit != svec.end())
        {
            if(book_name == *sit)  //如果输入的书目在vector,判断再判断该书
            {
                if(!sset.count(*sit)) //如果book_name未被记录为已读
                {
                    sset.insert(*sit); //将此book_name标记为已读
                    //svec.erase();将此数删除
                }
                else
                {
                    cout << "the book you choose has read,pls input again:" << endl;
                }
                break;
            }
            ++sit;
        }
        if(sit == svec.end())
        {
            cout << "the book you choose not included in vector,pls input again:" << endl;
            continue;
        }
    }

    //输出set容器中已读书
    set<string>::iterator set_str = sset.begin();
    for(; set_str != sset.end(); ++set_str)
        cout << *set_str << ' ';
    cout << endl;

    //输出vector容器中未读的书
    vector<string>::iterator sit = svec.begin();
    for(; sit != svec.end(); ++sit)
        cout << *sit << ' ';
    cout << endl;

    return 0;
}
注:1)答案用到了随机数来从vector对象生成一个书目,比在vector查找书目方便多了。对随机数使用不太熟悉。

#include <iostream>
#include <vector>
#include <set>
#include <string>
#include <cstdlib>
#include <ctime>
using namespace std;

int main()
{
    vector<string> books;
    set<string> readedBooks;
    string name;

    //建立保存未来6个月要阅读的书名的vector对象
    cout << "enter names for books you'd like to read:(ctrl+z to end)" << endl;
    while(cin >> name)
        books.push_back(name);

    cin.clear();//使流对象有效

    bool timeOver = false;
    string answer, bookName;
    //用当前系统时间设置随机数发生器种子
    srand((unsigned)time(NULL));

    //模拟随时间的流逝而改变读书记录
    while(!timeOver && !books.empty())
    {
        //时间未到6个月还有书没有读过
        cout << "would you like to read to book?(Yes/No)" << endl;
        cin >> answer;

        if(answer[0] == 'y' || answer[0] == 'Y')
        {
            //在vector中随机选择一本书
            int i = rand() % books.size(); //产生一个伪随机数
            bookName = books[i];
            cout << "You can read this book:"
                 << bookName << endl;
            readedBooks.insert(bookName);//将该书放入已读集合
            books.erase(books.begin() + i); //从vector对象中删除该书

            cout << "did you read it?(Yes/No)" << endl;
            cin >> answer;
            if(answer[0] == 'n' || answer[0] == 'N')
            {
                //没有读这本书
                readedBooks.erase(bookName);//从已读集合中删除该书
                books.push_back(bookName);//将该书重新放入vector中
            }
        }
        cout << "Time over?(Yes/No)" << endl;
        cin >> answer;
        if(answer[0] == 'y' || answer[0] == 'Y')
        {
            //虚拟的6个月结束了
            timeOver = true;
        }
    }

    if(timeOver)//虚拟的6个月结束了
    {
        //输出已读书目
        cout << "books read:" << endl;
        for(set<string>::iterator sit = readedBooks.begin();
                sit != readedBooks.end();++sit)
            cout << *sit << endl;

        //输出还没有读的书目
        cout << "books not read:" << endl;
        for(vector<string>::iterator vit = books.begin();
                vit != books.end(); ++vit)
            cout << *vit << endl;
    }
    else
        cout << "congraturations! You have read all these books."
             << endl;

    return 0;
}

10.6 容器的综合应用:文本查询程序

/****************************************************
时间:2013-8-13
习题:将读取用户指定的任意文本文件,然后允许用户从该文件中查找单词。查询的结构是该单词出现的次数,并列出每次所在的行。如果某单词在同一行中多次出现,程序将只显示该行一次。行号按升序显示。
如:查找单词“element”
输出:
element occurs 125 times
     (line 62) element with a given key.
    (line 64) second element with the same key
    (line 153) element |==| operator
    (line 250)  the element type
    (line 398) corresponding element
*****************************************************************/

这个程序搞了一两天,很多问题:对set和map的理解和使用不熟悉;对类的接口,返回值等不熟悉。程序用一个TextQuery类来实现,使用了几种容器。

1.数据结构

1)使用vector<string>来存储输入文本文件的副本。注意,vector的每个元素都是每一行的文本,这里可以用getline来每行每行的读入;还是就是,在输出某一行时,只需以行号为下标来输出该行所在的元素。(在后面的string TextQuery::text_line(line_no line) const)这个函数中,我用的循环来查找行号与line_no相等的下标,再输出,没有必要,可以直接根据line_no(即下标)访问来输出对应的文本行。)

2)将每个单词所在的行号存储在一个set容器对象中。使用set就可确保每行只有一个条目,而且行号将自动按升序排列。(关于这里,有点迷糊,为什么要用set对象来保存行号?)

3)还使用一个map容器将每个单词和set容器对象关联起来。在find在map容器中查找单词时,可以知道该单词对应的行号。

2.接口

书上一段话,有点不理解:“查询的过程非常简单:使用下标访问map对象获取关联的set对象即可。唯一的问题是如何返回找到的set对象。安全的设计方案是返回该set对象的副本。但如此一来,意味着要复制set中的每一个元素。如果处理的是一个相当大的文件,则复制set对象的代价非常昂贵。其中可行的方法:返回一个pair对象,存储一对指向set中元素的迭代器;或者返回set对象的const引用。

“TextQuery.h"部分

//TextQuery.h部分
#include <iostream>
#include <map>
#include <set>
#include <cstring>
#include <vector>
#include <fstream>
using namespace std;

class TextQuery
{
public:
    typedef vector<string>::size_type line_no;//修改1:行号为vector<>::size_type 类型,这里可以用typedef

    //void read_file(ifstream &is);  //
    void read_file(ifstream &is)
    {
        store_file(is);    //修改2:添加了两个private函数(不知道为什么?)
        build_map();
    }

    //const set<line>& run_query(string s);
    set<line_no> run_query(string &s) const; //修改3:返回值为set对象的const引用?

    //const int text_line(int line);
    //line_no text_line(line_no); //返回该行对应的文本行
    string text_line(line_no) const; //修改4:定义常成员函数const放在后面
    //void store_file(ifstream&);
private:
    //修改
    //实现read_file
    void store_file(ifstream&);  //store input file
    void build_map();            //
    //remeber the whole input file
    vector<string> lines_of_text;

    //set<line>;
    //map word to set of lines on which it occurs
    map<string, set<line_no> > word_map;
    //map<string, set<line> > my_map;

    //string str; //每行文本
    //int line;   //行号
};
TextQuery.cpp部分

void TextQuery::store_file(ifstream &is)
{
    string  line;

    //①类的私有函数怎么访问私有变量?
    //  while(is >> word)
    //    lines_of_text.push(word);

    while(getline(is, line))
        line_of_text.push_back(line);

}
//②输入完毕后,read_file将创建关联单词与行号的map容器?
//前面将每行的文本读到vector中,vector中的每一个元素是一行文本,
//怎么提取这一行文本的所有单词并且都记录为在当前行号?
//
void TextQuery::build_map()
{
    //向map<string,set<line_no> >插入元素?
    //vector<string>::size_type ix = 0; //此处元素string为一行文本?

    line_no ix = 0; //类里已用typedef定义为line_no

    //使用istringstream将每一行的单词提取出来
    for(; ix != line_of_text.size(); ++ix)
    {
        //istringstream stream(*iter);//?可以改为用下标来访问vector元素
        istringstream stream(lines_of_text[ix]);
        string word;
        while(stream >> word)
        {
//            map<string, set<line_no> >::const_iterator map_it =
//            word_map.find(word);
//
//            if(map_it != word_map.end())
//                map_it->second = ix;

            //word_map.insert(make_pair(word, ix));//自己写的         
                      word_map[word].insert(ix);//答案?这句没想明白
        }
    }
}
//返回该行的文本
string TextQuery::text_line(line_no line) const
{
    //访问vector,根据line_no输出对应的行string?注意:行号与该行的文本行对应

//    vector<string>::size_type ix = 0;
//
//    for(; ix != lines_of_text.size(); ++ix)
//        if(ix == line)
//            return lines_of_text[ix];

    //不需要遍历vector,形参已经给出下标(即行号)来访问
    if(line < lines_of_text.size())
        return lines_of_text[line];
    throw out_of_range("line number of range");
}

//set对象包含出现该string对象的所有行的行号
set<line_no> TextQuery::run_query(string &s) const
{
    //map对象的遍历
    map<string, set<line_no> >::const_iterator =
        map_it = word_map.begin();

    while(map_it != word_map.end())
    {
        if(map_it->first == s)
            return map_it->second;
        ++map_it;
    }
    return 0;
}

run_query()函数答案用到find函数来查找(),使用find好处,如果指定键的元素不存在,就不会在容器中创建新的元素。(可是没有明白下面为什么不用循环,每次find只能返回一个值,怎么返回得到查找单词的所有行好的?还有这个map容器,一个键不是只能对应一个值吗?word[word].insert(ix),不理解?)

//答案:

set<TextQuery::line_no> TextQuery::run_query(const string &query_word) const
{
    // Note: must use find and not subscript the map directly
    // to avoid adding words to word_map!
    map<string, set<line_no> >::const_iterator
                          loc = word_map.find((query_word));
    if (loc == word_map.end())
        return set<line_no>();  // not found, return empty set
    else
        // fetch and return set of line numbers for this word
        return loc->second;
}





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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值