cppPrimer学习11th
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)
;
}