cppPrimer学习11th

Posted zongzi10010

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了cppPrimer学习11th相关的知识,希望对你有一定的参考价值。


title: cppPrimer学习11th
date: 2020/1/11 14:07:51
toc: true
---

cppPrimer学习11th

知识点

  • map与pair的关系: map的元素是pair,一个pair就是一个模版结构体,有两个数据对

11.1

描述map和vector的不同

map 存储的是 name--value
vector 存的是value

11.2

分别给出最适合使用list、vector、deque、map以及set的例子

list 需要中间插入的
vector 尾部插入,随机访问
deque  头尾插入,随机访问
map    需要按照name查询的
set    单一序列,黑白名单

11.3

11.4

/*
11.3 编写你自己的单词计数程序
11.4 忽略大小写和标点。例如,“example.”、"example,"和"Example"应该递增相同的计数器
*/

#include "../include/include.h"
#include <ctype.h>

int main(int argc, char const *argv[])
{
    map<string, size_t> dic;
    string ch;

    while (cin >> ch)
    {
        for (auto &c : ch)
            c = tolower(c);

        auto lst = find_if(ch.crbegin(), ch.crend(), [](const char &ch) { return !ispunct(ch); });
        ch.erase(lst.base(), ch.cend());

        //cout << ch << endl;

        dic[ch]++;
    }

    for (auto &ch : dic)
    {
        cout << ch.first << "  has " << ch.second << endl;
    }
    while (1)
        ;

    return 0;
}

11.5

解释map和set的区别。你如何选择使用哪个

map   name--value  字典 
set   value

11.6

解释set和list的区别。你如何选择使用哪个

set     唯一性,不支持push等操作
list    非唯一性

11.7

/**
 * 定义一个map,关键字是家庭的姓,值是一个vector,保存家中孩子们的名。编写代码,实现添加新的家庭以及向已有家庭中添加新的孩子
 * 
 * family child input
    1 2
    2 21
    1 1
    3 123
    8 12345678
    1 5
    2 22
    ^Z
    family: 1
    chile: 2,1,5,
    family: 2
    chile: 21,22,
    family: 3
    chile: 123,
    family: 8
    chile: 12345678,
 * 
 * 
 * */



#include "../include/include.h"
#include <map>
int main(int argc, char const *argv[])
{
    map<string, vector<string>> people;

    string family, child;

    while (cin >> family >> child)
    {
        people[family].push_back(child);
    }

    for (auto ch : people)
    {
        cout << "family: " << ch.first << endl;
        cout << "chile: ";
        for (auto c : ch.second)
        {
            cout << c << ",";
        }
        cout << endl;
    }

    while (1)
        ;
    return 0;
}

11.8

/*
编写一个程序,在一个vector而不是一个set中保存不重复的单词。使用set的优点是什么
*/

#include "../include/include.h"
int main(int argc, char const *argv[])
{
    vector<string> v;
    string ch;
    while (cin >> ch)
    {
        v.push_back(ch);
    }

    sort(v.begin(), v.end());
    auto lst = unique(v.begin(), v.end());
    v.erase(lst, v.end());
    print(v);

    while (1)
        ;
    return 0;
}

11.9

//定义一个map,将单词与一个行号的list关联,list中保存的是单词所出现的行号。

map<string, list<int> >
    

11.10

可以定义一个vector::iterator到int的map吗?         可以
    list::iterator到int的map呢 ?           不可以,因为list的it 没有 < 和=
 
// 但是是可以编译过去的    
map<vector<int>::iterator, int> m;
map<list<int>::iterator, int> l;    

11.11

using Less = bool (*)(Sales_data const&, Sales_data const&);
multiset<Sales_data, Less> bookstore(less);


typedef bool (*pf) (const Sales_data &, const Sales_data &);
multiset<Sales_data, pf> bookstore(compareTsbn)

multiset< Sales_data, bool(*)(const Sales_data& a Sales_data& b) >

11.12

11.13

/*写程序,读入string和int序列,将每个string和int序列存入一个pair中,pair保存在一个vector中*/
/*至少有三种创建pair的方法*/
/*vec.emplace_back(str, i); //!! easiest way.  我没想到的方法*/

#include "../include/include.h"
int main(int argc, char const *argv[])
{
    vector<pair<string, int>> v;

    string s;
    int i;

    while (cin >> s >> i)
    {
        //v.push_back(make_pair(s, i));
        //v.push_back({s, i});
        v.push_back(pair<string, int>(s, i));
        /*vec.emplace_back(str, i); //!! easiest way.  我没想到的方法*/
    }

    for (auto ch : v)
    {
        cout << ch.first << "--" << ch.second << endl;
    }
    while (1)
    {
        /* code */
    }

    return 0;
}

11.14

/*
扩展你在11.7节练习中编写的孩子姓到名的map,添加一个pair的vector,保存孩子的名字和生日
jack 1 11-12
jack 2 11-13
cc 1 1-5
cc 5 5-8
jack 21 15-55
^Z
family: cc
child: 1,1-5
5,5-8

family: jack
child: 1,11-12
2,11-13
21,15-55
*/

/**
 * 11.7 定义一个map,关键字是家庭的姓,值是一个vector,保存家中孩子们的名。编写代码,实现添加新的家庭以及向已有家庭中添加新的孩子
 * 
 * family child input
    1 2
    2 21
    1 1
    3 123
    8 12345678
    1 5
    2 22
    ^Z
    family: 1
    chile: 2,1,5,
    family: 2
    chile: 21,22,
    family: 3
    chile: 123,
    family: 8
    chile: 12345678,
 * 
 * 
 * */

#include "../include/include.h"
#include <map>
int main(int argc, char const *argv[])
{
    map<string, vector<pair<string, string>>> people; // modify

    string family, child, day;

    while (cin >> family >> child >> day) // modify
    {
        people[family].push_back(make_pair(child, day)); // modify
    }

    for (auto ch : people)
    {
        cout << "family: " << ch.first << endl;
        cout << "child: ";
        for (auto c : ch.second)
        {
            cout << c.first << "," << c.second << endl; // modify
            ;
        }
        cout << endl;
    }

    while (1)
        ;
    return 0;
}

11.15

map<int, vector<int>>::mapped_type  vector<int>
map<int, vector<int>>::key_type     int
map<int, vector<int>>::value_type   pair<int, vector<int> >    
    

11.16

/*
使用一个map迭代器编写一个表达式,将一个值赋予一个元素
*/

#include "../include/include.h"
#include <map>

int main(int argc, char const *argv[])
{
    map<int, string> v;
    v[10] = "123";
    auto it = v.begin();
    cout << it->second << endl;

    it->second = "555";
    cout << it->second << endl;

    while (1)
        ;
    return 0;
}

11.17

假定c是一个string的multiset,v是一个string的vector,解释下面的调用。指出每个调用是否合法

copy(v.begin(), v.end(), inserter(c, c.end()));     //不允许对set insert,非法
copy(v.begin(), v.end(), back_inserter(c);          //不允许对set insert,非法
copy(c.begin(), c.end(), inserter(v, v.end()));     //合法
copy(c.begin(), c.end(), back_inserter(v);          //合法

11.18

写出第382页循环中map_it的类型,不要使用auto或decltype.
pair<const string, size_t>::iterator

11.19

/*
定义一个变量,通过11.2.2节中的名为bookstore的multiset的调用begin()来初始化这个变量。
写出变量的类型,不要使用auto或decltype
*/
//multiset<Sales_data,decltype(compareIsbn)*> bookstore(compareIsbn);

using CompareFn =bool(*)(const Sales_data& a,const Sales_data& b);

multiset<Sales_data,CompareFn>::iterator it=bookstore.begin();

11.20


/*
重写11.1节练习的单词计数程序,使用inserter代替下标操作。你认为哪个程序更容易编写和阅读?
*/

/*
11.3 编写你自己的单词计数程序
11.4 忽略大小写和标点。例如,“example.”、"example,"和"Example"应该递增相同的计数器
*/

#include "../include/include.h"
#include <ctype.h>

int main(int argc, char const *argv[])
{
    map<string, size_t> dic;
    string ch;

    while (cin >> ch)
    {
        for (auto &c : ch)
            c = tolower(c);

        auto lst = find_if(ch.crbegin(), ch.crend(), [](const char &ch) { return !ispunct(ch); });
        ch.erase(lst.base(), ch.cend());

        //dic[ch]++;
        auto it = dic.insert({ch, 1});
        if (false == it.second)
        {
            (it.first->second)++;
        }
    }

    for (auto &ch : dic)
    {
        cout << ch.first << "  has " << ch.second << endl;
    }
    while (1)
        ;

    return 0;
}

11.21

假定word_count是一个string到size_t的map,word是一个string,解释下面循环的作用
    while(cin>>word)
     ++word_count.insert({word,0}).first->second;

word_count.insert({word,0}).first 返回的是map的迭代器
    word_count.insert({word,0}).first->second;  map<1,2> 2就是mapped_type 值
     ++word_count.insert({word,0}).first->second;  值++

若单词已存在容器中,它指向已有元素;否则,它指向新插入的元素。
统计输入的单词次数
    
    

11.22

给定一个map<string, vextor<int>>, 对此容器的插入一个元素的insert版本,写出其参数类型和返回类型
    
 参数类型 pair<string,vextor<int> >
 返回类型 pair< map<string,vextor<int>>::iterator,bool>

11.23

/*
11.7练习中的map以孩子的姓为关键字,保存他们的名的vector,用multimap重写此map。
*/

/**
 * 定义一个map,关键字是家庭的姓,值是一个vector,保存家中孩子们的名。编写代码,实现添加新的家庭以及向已有家庭中添加新的孩子
 * 
*/

#include "../include/include.h"
#include <map>
int main(int argc, char const *argv[])
{
    //map<string, vector<string>> people;
    multimap<string, string> people;

    string family, child;

    while (cin >> family >> child)
    {
        people.insert({family, child});
    }

    for (auto ch : people)
    {
        cout << "family: " << ch.first << endl;
        cout << "chile: " << ch.second << endl;
    }

    while (1)
        ;
    return 0;
}

11.24

下面的程序完成什么功能?
map<int, int> m;
m[0]=1;

map 中如果有0关键字的话,赋值为1
否则
    1. 创建m[0]=0
    2. 再赋值m[0]=1

11.25

对比下面程序与上一题程序
vector<int> v;
v[0]=1;

直接赋值v[0],但是这里的vector的capacity是0 size也是0

11.26

/*可以用什么类型来对一个map进行下标操作?
下标运算符返回的类型是什么?请给出一个具体例子 -- 
即,定义一个map,然后写出一个可以用来对map进行下标操作的类型以及下标运算符会返回的类型*/

#include "../include/include.h"

int main(int argc, char const *argv[])
{
    map<int, int> m;

    //map<int, int>::iterator in = m.begin();
    m[0] = 1;
    map<int, int>::mapped_type elem = m[0];
    cout << elem << endl;

    // 不懂
    //type to subscript: St3mapIiiSt4lessIiESaISt4pairIKiiEEE
    //returned from the subscript operator: i
    std::cout << "type to subscript: " << typeid(m).name() << std::endl;
    std::cout << "returned from the subscript operator: " << typeid(decltype(m[0])).name() << std::endl;

    while (1)
        ;
    return 0;
}

11.27

对于什么问题你会使用count来解决?什么时候你又会选择find呢?

find  关心是否有
count 对于multimap,multiset 统计数量,或者打印

11.28

map<string, vector<int> >m;
map<string, vector<int> >::iterator f=m.find(string)

11.29

如果给定的关键字不在容器中,upper_bound、lower_bound 和 equal_range 分别会返回什么
upper_bound 和 lower_bound  返回相等的迭代器,指向一个不影响排序的关键字的插入位置,如果查找的元素大于所有元素则是end
equal_range 返回 pair<it,it> 指向关键字可插入的位置

11.30

for(auto pos=authors.equal_range(xxx); pos.first != pos.second; ++pos.first)
 cout<<pos.first->second<<endl;
 
pos.first 迭代器指向了查到的元素  迭代器是map的迭代器也就是 value_type=pair<key,value>
pos.first->second  指向了 mapped_value
 

11.32

/*
使用上一题定义的multimap编写一个程序,按字典序打印作者列表和他们的作品。
**/

/*编写程序,定义一个作者及其作品的multimap。使用find在multimap中查找一个元素并用erase删除它。确保你的程序在元素不在map中时也能正常运行。*/

#include "../include/include.h"

void printMap(const multimap<string, string> &m)
{
    for (auto ch : m)
        cout << ch.first << ":" << ch.second << endl;
}

int main(int argc, char const *argv[])
{
    multimap<string, string> m;
    m.insert({"a", "1"});
    m.insert({"b", "2"});
    m.insert({"c", "3"});
    m.insert({"d", "4"});
    m.insert({"a", "2"});
    m.insert({"a", "3"});
    m.insert({"a", "4"});
    printMap(m);

    map<string, multiset<string>> m2;
    for (auto ch : m)
    {
        m2[ch.first].insert(ch.second);
    }
    for (auto ch : m2)
    {
        cout << ch.first << ":" << endl;
        for (auto l : ch.second)
            cout << l << "+";
        cout << endl;
    }

    while (1)
        ;
    return 0;
}

11.33

//实现你自己版本的单词转换程序
#include "../include/include.h"
#include <fstream>
#include <sstream>

map<string, string> build_map(ifstream &if_map)
{
    map<string, string> m;
    string key, value;
    while (if_map >> key && getline(if_map, value))
    {
        // remove 只是移动元素 ,没有删除,返回的是最后的符合的迭代器
        auto it = remove_if(value.begin(), value.end(), [](const char ch) { return (ch == ' '); });
        value.erase(it, value.end());

        if (value.size() > 0)
        {
            m[key] = value;
        }
    };

    return m;
}

string transform(const map<string, string> &m, const string &txt)
{
    auto it = m.find(txt);
    if (it != m.end())
        return it->second;
    else
        return txt;
}

void word_transform(ifstream &map, ifstream &input)
{
    auto string_map = build_map(map);
    string txt_line;
    while (getline(input, txt_line))
    {
        istringstream words(txt_line);
        string ch;
        while (words >> ch)
        {
            cout << transform(string_map, ch) << " ";
        }
        cout << endl;
    }
}

int main(int argc, char const *argv[])
{
    ifstream if_rules("E:\rules.txt");
    ifstream if_txt("E:\me.txt");
    if (if_rules && if_txt)
    {
        word_transform(if_rules, if_txt);
    }
    else
    {
        if (!if_rules)
            cout << " open rules " << endl;
        if (!if_txt)
            cout << " open txt " << endl;
    }
    while (1)
        ;
}

11.34

如果你将transform函数中的find替换为下标运算符,会发生什么情况
    map中要查找的key不存在会添加

11.35

trans_map[key] = value.substr(1);
    改为trans_map.insert({key, value.substr(1)})
        
使用下标进行插入,则value是最后文件中key对应的最后一个短语。
使用insert,则key对应的是第一个短语。        

11.36

我们的程序并没有检查输入文件的合法性,特别是,它假定转换规则文件中的规则都是有意义的。
    如果文件中的某一行包含一个关键字、一个空格,然后就结束了,会发生什么?
    预测程序的行为并进行验证,再与你的程序进行比较。

    我的程序有判断size,如果没有判断的话,测试一下
    //if (value.size() > 0)  没有发生任何异常,没有进来 不会添加映射,也就是 比如有规则 lo ,不会替换lo为空白的
    

11.37

无序版本通常性能更好
有序版本的优势是维护了关键字的顺序。
当元素的关键字类型没有明显的序关系,或是维护元素的序代价非常高时,无序容器非常有用。
但当应用要求必须维护元素的序时,有序版本就是唯一的选择

11.38

/*
用unordered_map重写单词计数程序和单词转换程序
11.4.cpp
11.33.cpp
*/

/*
11.3 编写你自己的单词计数程序
11.4 忽略大小写和标点。例如,“example.”、"example,"和"Example"应该递增相同的计数器
*/

#include "../include/include.h"
#include <ctype.h>
#include <fstream>
#include <sstream>
#include <unordered_map>

void test11_4()
{
    unordered_map<string, size_t> dic;
    string ch;

    while (cin >> ch)
    {
        for (auto &c : ch)
            c = tolower(c);

        auto lst = find_if(ch.crbegin(), ch.crend(), [](const char &ch) { return !ispunct(ch); });
        ch.erase(lst.base(), ch.cend());

        //cout << ch << endl;

        dic[ch]++;
    }

    for (auto &ch : dic)
    {
        cout << ch.first << "  has " << ch.second << endl;
    }
    while (1)
        ;
}

unordered_map<string, string> build_map(ifstream &if_map)
{
    unordered_map<string, string> m;
    string key, value;
    while (if_map >> key && getline(if_map, value))
    {
        // remove 只是移动元素 ,没有删除,返回的是最后的符合的迭代器
        auto it = remove_if(value.begin(), value.end(), [](const char ch) { return (ch == ' '); });
        value.erase(it, value.end());

        if (value.size() == 0)
            ; //cout << "size=0" << endl;
        else
        {
            m[key] = value;
        }
    };

    return m;
}

string transform(const unordered_map<string, string> &m, const string &txt)
{
    auto it = m.find(txt);
    if (it != m.end())
        return it->second;
    else
        return txt;
}

void word_transform(ifstream &map, ifstream &input)
{
    auto string_map = build_map(map);
    string txt_line;
    while (getline(input, txt_line))
    {
        istringstream words(txt_line);
        string ch;
        while (words >> ch)
        {
            cout << transform(string_map, ch) << " ";
        }
        cout << endl;
    }
}

int main(int argc, char const *argv[])
{
    ifstream if_rules("E:\rules.txt");
    ifstream if_txt("E:\me.txt");
    if (if_rules && if_txt)
    {
        word_transform(if_rules, if_txt);
    }
    else
    {
        if (!if_rules)
            cout << " open rules " << endl;
        if (!if_txt)
            cout << " open txt " << endl;
    }
    while (1)
        ;
}

以上是关于cppPrimer学习11th的主要内容,如果未能解决你的问题,请参考以下文章

cppPrimer学习-9th

cppPrimer学习16th

cppPrimer学习8th

c_cpp 审查CppPrimer#104

ThymeLeaf 片段在错误 th:if 上执行

thymeleaf引入公共页面的某个片段