C++primer学习笔记及作业答案之第十章

笔记:

1.概述

迭代器令算法不依赖于容器,但算法依赖于元素类型的操作。

标准库通过算法和数据结构的分离来实现泛型。

算法永远不会改变底层容器的大小。

2.初识泛型算法

C风格字符串本质是char *类型,用==比较两个char *对象,只是检查两个指针值是否相等,即地址是否相等,而不会比较其中字符是否相同

标准库算法从来不直接操作容器,它们只操作迭代器,从而间接访问容器。能不能插入和删除元素,不在于算法,而在于传递给它们的迭代器是否具有这样的能力。

泛型,算法与操作的数据结构分离,而迭代器在之间架起了桥梁。

3.定制操作

谓词是一个可用的表达式,其返回结果是一个能用作条件的值。

一个lambda表达式表示一个可调用的代码单元。可以将其理解为未命名的内联函数。

lambda必须使用尾置返回来指定返回类型,我们可以忽略列表和返回类型,但必须永远包含捕获列表和函数体。

lambda在可以在函数内部定义。尽量保持lambda的变量捕获简单化。

bind函数可以看做一个通用的函数适配器,他接受一个可调用对象,生成一个新的可调用对象来适应原对象的参数列表。

10.4 在探迭代器

流迭代器不支持递减运算。

不能从一个forward_list或一个流迭代器创建反向迭代器。

当我们从一个普通迭代器初始化一个反向迭代器,或是给一个反向迭代器赋值时,结果迭代器与原迭代器指向的并不是相同的元素。

用流迭代器和copy输出int序列。

反向迭代器:vector::reverse_iterator r_end(ivec.begin())。

10.6 特定容器算法

对于list和forward_list,应该优先使用成员函数版本的算法而不是通用算法。

splice成员是链表数据结构所特有的。

链表特有的操作会改变容器。

课后习题:

练习 10.1:头文件algorithm中定义了一个名为count 的函数,它类似find,接受一对选代器和一个值作为参数。count 返回给定值在序列中出现的次数。编写程序,读取int 序列存入vector 中,打印有多少个元素的值等于给定值。

//练习 10.1
#include 
#include 
#include 
#include 

using namespace std;

int main(int argc, char * argv[])
{
	//检测是否输入文件名
	if (argc != 2)
	{
		cout << "请输入文件名!" << endl;
		return -1;
	}
	
	ifstream in(argv[1]);
	//将文件绑定到输入流上,记得检查输入流是否有效
	if (!in)
	{
		cout << "无法打开文件!" << endl;
		return -1;
	}
	
	vector	ivec;
	int val;
	while (in >> val)
	{
		ivec.push_back(val);
	}

	int num;
	cout << "请输入您想查找的数字:";
	cin >> num;
	cout << num << " 共出现了:";
	cout << count(ivec.begin(), ivec.end(), num) << endl;

	system("pause");
	return 0;
}

练习 10.2:重做上一题,但读取string 序列存入list 中。

//练习 10.2
#include 
#include 
#include 
#include 
#include 

using namespace std;

int main(int argc, char * argv[])
{
	//检测是否输入文件名
	if (argc != 2)
	{
		cout << "请输入文件名!" << endl;
		return -1;
	}
	
	ifstream in(argv[1]);
	//将文件绑定到输入流上,记得检查输入流是否有效
	if (!in)
	{
		cout << "无法打开文件!" << endl;
		return -1;
	}
	
	list slis;
	string s;
	while (in >> s)
	{
		slis.push_back(s);
	}

	string ss;
	cout << "请输入您想查找的字符:";
	cin >> ss;
	cout << ss << " 共出现了:";
	cout << count(slis.begin(), slis.end(), ss) << endl;

	system("pause");
	return 0;
}

练习 10.3:用accumulate 求一个vector

//练习 10.3
#include 
#include 
#include 
#include 

using namespace std;

int main(int argc, char * argv[])
{
	//检查输入文件名是否正确
	if (argc != 2)
	{
		cout << "请正确的输入文件名!" << endl;
		return -1;
	}

	//将输入的文件名与文件输入流绑定在一起,并检查输入流是否有效
	ifstream in(argv[1]);
	if (!in)
	{
		cout << "无法打开文件!" << endl;
		return -1;
	}

	vector ivec;
	int num;
	while (in >> num)
	{
		ivec.push_back(num);
	}

	int sum = accumulate(ivec.begin(), ivec.end(), 0);
	cout << "和为:" << sum << endl;

	system("pause");
	return 0;
}

练习 10.4:假定v 是一个vector

答:accumulate的第三个参数是和的初始值,它还决定了函数的返回类型,以及函数中使用哪个加法运算符。

因此,本题中的调用是错误的,第三个参数0告知accumulate,和是整型的,使用整型加法运算符。正确的调用方法是将0.0作为第三个参数传递给accumulate。

练习 10.5:在本节对名册(roster)调用equal 的例子中,如果两个名册中保存的都是c 风格字符串而不是string,会发生什么?

答:equal使用==运算符比较两个序列中的元素。string类重载了==,可比较两个字符串是否长度相等且其中元素对位相等。而C风格字符串本质是char *类型,用==比较两个char *对象,只是检查两个指针值是否相等,即地址是否相等,而不会比较其中字符是否相同。所以,只有当两个序列中的指针都指向相同的地址时,equal才会返回true,否则,即使字符串内容完全相同,也会返回false。

练习 10.6:编写程序,使用fill_n 将一个序列中的int 值都设置为0 。

//练习 10.6
#include 
#include 
#include 
#include 

using namespace std;

int main(int argc, char * argv[])
{
	ifstream in(argv[1]);
	if (!in)
	{
		cout << "打开输入文件失败!" << endl;
		return -1;
	}

	vector vi;
	int val;
	while (in >> val)
	{
		vi.push_back(val);
		cout << val << " ";
	}
	cout << endl;

	fill_n(vi.begin(), vi.size(), 0);
	for (auto iter = vi.begin(); iter != vi.end(); ++iter)
	{
		cout << *iter << endl;
	}

	system("pause");
	return 0;
}

练习 10.7:下面程序是否有错误?如果有,请改正。

(a) vector vec; list lst; int i;
    while(cin >> i)
        lst.push_back(i);
    copy(lst.begin(), lst,end(), vec.begin());

(b) vector vec;
    vec.reserve(10);
    fill_n(vec.begin(), 10, 0);

(a)有错误,copy算法要求目标序列至少要包含与源序列一样多的元素,而此程序中,vec进行缺省初始化,它是空的,copy无法进行。可以将第三个参数改为back_inserter(vec),通过它,copy算法即可将lst中的元素拷贝插入到lst的末尾。

(b)也有错误。此时,vec仍然为空,没有任何元素。而算法又不具备向容器添加元素的能力,因此fill_n仍然失败。这里还是将第一个参数改成back_inserter(vec)来让fill_n有能力向vec添加元素。

练习 10.8:本节提到过,标准库算法不会改变它们所操作的容器的大小。为什么使用back_inserter 不会使这一断言失效?

答:标准库算法根本不知道有“容器”这个东西。它们只接受迭代器参数,运行于这些迭代器之上,通过这些迭代器来访问元素。

因此,当传递给算法普通迭代器时,这些迭代器只能顺序或者随机访问容器中的元素,造成的效果就是算法只能读取元素、改变元素、移动元素,但无法添加或者删除元素。

当我们传递给算法插入器,例如back_inserter时,由于这类迭代器能调用下层容器的操作来向容器插入元素,造成算法执行的效果就是向容器中添加了元素。

标准库算法从来不直接操作容器,它们只操作迭代器,从而间接访问容器。能不能插入和删除元素,不在于算法,而在于传递给它们的迭代器是否具有这样的能力。

练习 10.9:实现你自己的elimDups 。测试你的程序, 分别在读取输入后、调用unique后以及调用erase 后打印vector 的内容。

//练习 10.9

#include 
#include 
#include 
#include 
#include 

using namespace std;

void elimDups(vector & words)
{
	cout << "原始vector的元素为:" << endl;
	//打印原始vector
	for (auto & r : words)
		cout << r << " ";
	cout << endl;
	//sort对vector中的元素进行重排序
	sort(words.begin(), words.end());

	cout << "排序之后的vector元素为:" << endl;
	//打印排序之后的vector
	for (auto & r : words)
		cout << r << " ";
	cout << endl;

	//unique并没有删除元素,只是让不重复的元素出现在前面,
	//返回一个指向不重复范围末尾的迭代器,
	//即相当于把重复的元素都放在了后面
	auto end_unique = unique(words.begin(), words.end());
	cout << "unique之后的vector元素为:" << endl;
	for (auto & r : words)
		cout << r << " ";
	cout << endl;

	//最后利用容器自身的操作删除重复的元素
	words.erase(end_unique, words.end());
	cout << "erase之后的vector元素为:" << endl;
	for (auto & r : words)
		cout << r << " ";
	cout << endl;
}

int main(int argc, char * argv[])
{
	if (argc != 2)
	{
		cout << "请正确输入文件名!" << endl;
		return -1;
	}

	//打开文件
	ifstream in(argv[1]);
	if (!in)
	{
		cout << "无法打开文件!" << endl;
		return -1;
	}

	vector svec;
	string s;
	while (in >> s)
	{
		svec.push_back(s);
	}

	elimDups(svec);


	system("pause");
	return 0;
}

练习 10.10:你认为算法不改变容器大小的原因是什么?

答:泛型算法的一大优点是“泛型”,也就是一个算法可用于多种不同的数据结构,算法与所操作的数据结构分离。要做到算法与数据结构分离,重要的技术手段就是使用迭代器作为两者的桥梁。算法从不操作具体的容器,从而也就不存在与特定容器绑定,不适用于其他容器的问题。算法只操作迭代器,由迭代器真正实现对容器的访问。不同容器实现自己特定的迭代器,算法操作不同的迭代器就实现了对不同容器的访问。

因此,并不是算法应该改变或不改变容器的问题。为了实现与数据结构的分离,为了实现通用性,算法根本就不该知道容器的存在。算法访问数据的唯一通道是迭代器。是否改变容器大小,完全是迭代器的选择和责任。

练习 10.11:编写程序,使用stable_sort 和isShorter 将传递给你的elimDups版本的vector 排序。打印vector 的内容, 验证你的程序的正确性。

//练习 10.11

#include 
#include 
#include 
#include 
#include 

using namespace std;

bool isShorter(const string & s1, const string & s2)
{
	return s1.size() < s2.size();
}

inline void output(vector & svec)
{
	for (auto & r : svec)
		cout << r << " ";
	cout << endl;
}

void elimDups(vector & words)
{
	cout << "原始vector的元素为:" << endl;
	//打印原始vector
	output(words);
	//sort对vector中的元素进行重排序
	sort(words.begin(), words.end());

	cout << "排序之后的vector元素为:" << endl;
	//打印排序之后的vector
	output(words);

	//unique并没有删除元素,只是让不重复的元素出现在前面,
	//返回一个指向不重复范围末尾的迭代器,
	//即相当于把重复的元素都放在了后面
	auto end_unique = unique(words.begin(), words.end());
	cout << "unique之后的vector元素为:" << endl;
	output(words);

	//最后利用容器自身的操作删除重复的元素
	words.erase(end_unique, words.end());
	cout << "erase之后的vector元素为:" << endl;
	output(words);

	//用stable_sort来重排元素
	stable_sort(words.begin(), words.end(), isShorter);
	cout << "stable_sort之后的vector元素为:" << endl;
	output(words);

}

int main(int argc, char * argv[])
{
	if (argc != 2)
	{
		cout << "请正确输入文件名!" << endl;
		return -1;
	}

	//打开文件
	ifstream in(argv[1]);
	if (!in)
	{
		cout << "无法打开文件!" << endl;
		return -1;
	}

	vector svec;
	string s;
	while (in >> s)
	{
		svec.push_back(s);
	}

	elimDups(svec);


	system("pause");
	return 0;
}

练习 10.12:编写名为compareIsbn 的函数,比较两个Sales_data 对象的isbn() 成员。使用这个函数排序一个保存Sales_data 对象的vector。

inline bool compareIsbn(const Sales_data & lhs, const Sales_data & rhs)
{
    return lhs.isbn() < rhs.isbn();
}

练习 10.13:标准库定义了名为partition 的算法,它接受一个谓词,对容器内容进行划分,使得谓词为true 的值会排在容器的前半部分,而使谓词为false 的值会排在后半部分。算法返回一个法代器,指向最后一个使谓词为true 的元素之后的位置。编写函数,接受一个string,返回一个bool 值,指出string 是否有5 个或更多字符。使用此函数划分words 。打印出长度大于等于5 的元素。

//练习 10.13

#include 
#include 
#include 
#include 
#include 

using namespace std;

//利用迭代器输出vector中的元素。
inline void output_words(vector::iterator beg, vector::iterator end)
{
	for (auto iter = beg; iter != end; ++iter)
		cout << *iter << " ";
	cout << endl;
}

//长度超过5的单词
bool five_or_more(const string &s)
{
	return s.size() >= 5;
}
int main(int argc, char *argv[])
{
	ifstream in(argv[1]);
	if (!in){
		cout << "无法打开文件。" << endl;
		return -1;
	}

	string word;
	vector vec;
	while (in >> word)
		vec.push_back(word);
	//partition返回一个迭代器,指向最后一个使谓词为true的元素之后
	auto iter = partition(vec.begin(), vec.end(), five_or_more);
	output_words(vec.begin(), iter);
	cout << endl;

	system("pause");
	return 0;
}

练习 10.14:编写一个lambda,接受两个int ,返回它们的和。

// 练习 10.14
[](int a, int b){ return a + b; }

练习 10.15:编写一个lambda,捕获它所在函数的int,并接受一个int 参数。lambda应该返回捕获的int 和int 参数的和。

// 练习 10.15
#include 

using namespace std;

void add(int a)
{
	//可调用对象
	auto sum = [a](int b) { return a + b; };

	cout << sum(1) << endl;
}

int main()
{
	add(1);
	add(2);

	system("pause");
	return 0;
}

练习 10.16:使用lambda 编写你自己版本的biggies 。

// 练习 10.16

#include 
#include 
#include 
#include 
#include 

using namespace std;

string make_plural(size_t ctr, const string &word, const string &ending)
{
	return (ctr>1) ? word + ending : word;
}
void elimdups(vector &words)
{
	sort(words.begin(), words.end());
	auto end_unique = unique(words.begin(), words.end());
	words.erase(end_unique, words.end());
}

void biggies(vector &words, vector::size_type sz)
{
	elimdups(words);      //将words按字典序排序,删除重复词
	// 按长度排序,长度相同的单词维持字典序
	stable_sort(words.begin(), words.end(), [](const string &a, const string &b){return a.size()=sz的元素
	auto wc = find_if(words.begin(), words.end(), [sz](const string &a){return a.size() >= sz; });
	// 计算满足size()>=sz的元素的数目
	auto count = words.end() - wc;
	cout << count << " " << make_plural(count, "word", "s") << " of length " << sz << " or longer" << endl;
	// 打印长度大于等于给定值的单词,每个单词后面接一个空格
	for_each(wc, words.end(), [](const string &s){cout << s << " "; });
	cout << endl;
}

int main(int argc, char *argv[])
{
	ifstream in(argv[1]);
	if (!in){
		cout << "Open input file failed." << endl;
		return -1;
	}
	string str;
	vector vec;
	while (in >> str)
		vec.push_back(str);
	biggies(vec, 5);

	system("pause");
	return 0;
}

练习 10.17:重写10.3.1节练习10.12(第345 页)的程序, 在对sort 的调用中使用lambda来代替函数compareIsbn。

sort(Sales_data_object.begin(), Sales_data_object.end(), 
    [] (Sales_data & lhs, Sales_data & rhs) { return lhs.isbn() < rhs.isbn(); });

练习 10.18:重写biggies,用partition 代替find_if 。我们在10.3.1节练习10.13(第345 页)中介绍了partition 算法。

// 练习 10.18

#include 
#include 
#include 
#include 
#include 

using namespace std;

string make_plural(size_t ctr, const string &word, const string &ending)
{
	return (ctr>1) ? word + ending : word;
}
void elimdups(vector &words)
{
	sort(words.begin(), words.end());
	auto end_unique = unique(words.begin(), words.end());
	words.erase(end_unique, words.end());
}

void biggies(vector &words, vector::size_type sz)
{
	elimdups(words);      //将words按字典序排序,删除重复词
	// 获取一个迭代器,指向最后一个size()>=sz的元素的后面
	auto wc = partition(words.begin(), words.end(), [sz](const string &a){return a.size() >= sz; });
	// 计算满足size()>=sz的元素的数目
	auto count = wc - words.begin();
	cout << count << " " << make_plural(count, "word", "s") << " of length " << sz << " or longer" << endl;
	// 打印长度大于等于给定值的单词,每个单词后面接一个空格
	for_each(words.begin(), wc, [](const string &s){cout << s << " "; });
	cout << endl;
}

int main(int argc, char *argv[])
{
	ifstream in(argv[1]);
	if (!in){
		cout << "Open input file failed." << endl;
		return -1;
	}
	string str;
	vector vec;
	while (in >> str)
		vec.push_back(str);
	biggies(vec, 5);

	system("pause");
	return 0;
}

对于本题而言,如果使用find_if,要求序列已按字符串长度递增顺序排好序,而partition不要求序列已排序,他对所有字符串检查长度是否满足要求,将满足要求的字符串移动到序列前段,不满足条件的字符串都移动到满足条件的字符串之后。

练习 10.19:用stable_partition重写前一题的程序,与stable_sort 类似, 在划分后的序列中维持原有元素的顺序。

//练习 10.19

#include 
#include 
#include 
#include 
#include 

using namespace std;

string make_plural(size_t ctr, const string &word, const string &ending)
{
	return (ctr>1) ? word + ending : word;
}
void elimdups(vector &words)
{
	sort(words.begin(), words.end());
	auto end_unique = unique(words.begin(), words.end());
	words.erase(end_unique, words.end());
}

void biggies(vector &words, vector::size_type sz)
{
	elimdups(words);      //将words按字典序排序,删除重复词
	 按长度排序,长度相同的单词维持字典序
	//stable_sort(words.begin(), words.end(), [](const string &a, const string &b){return a.size()=sz的元素的后面
	auto wc = stable_partition(words.begin(), words.end(), [sz](const string &a){return a.size() >= sz; });
	// 计算满足size()>=sz的元素的数目
	auto count = wc - words.begin();
	cout << count << " " << make_plural(count, "word", "s") << " of length " << sz << " or longer" << endl;
	// 打印长度大于等于给定值的单词,每个单词后面接一个空格
	for_each(words.begin(), wc, [](const string &s){cout << s << " "; });
	cout << endl;
}

int main(int argc, char *argv[])
{
	ifstream in(argv[1]);
	if (!in){
		cout << "Open input file failed." << endl;
		return -1;
	}
	string str;
	vector vec;
	while (in >> str)
		vec.push_back(str);
	biggies(vec, 5);

	system("pause");
	return 0;
}

stable_partition和partition相比,就是对满足要求的元素还排了序。

练习 10.20:标准库定义了一个名为count_if 的算法。类似find_if ,此函数接受一对迭代器,表示一个输入范围,还接受一个谓词,会对输入范围中每个元素执行。count_if 返回一个计数值,表示谓词有多少次为真。使用count_if 重写我们程序中统计有多少单词长度超过6 的部分。

#ifndef MAKE_PLURAL_H
#define MAKE_PLURAL_H

#include 
 
using std::string;  //头文件中记得要加这个!

string make_plural(size_t ctr, const string &word, const string &ending)
{
	return (ctr>1) ? word + ending : word;
}

#endif

练习 10.21:编写一个lambda,捕获一个局部int 变量,并递减变量值,直至它变为0。一旦变量变为0,再调用lambda 应该不再递减变量。lambda应该返回一个bool值,指出捕获的变量是否为0 。

// 练习 10.21
#include 
#include 

using namespace std;

void mutable_lambda()
{
	int i = 5;
	auto f = [i]() mutable -> bool { if (i > 0) { --i; return false; } else return true; };

	for (int j = 0; j < 6; ++j)
	{
		//调用lambda,lambda的调用方式和普通函数的调用方式一样
		cout << f() << " ";
	}
	cout << endl;
}

int main()
{
	mutable_lambda();

	system("pause");
	return 0;
}

练习 10.22:重写统计长度小于等于6 的单词数量的程序,使用函数代替lambda 。

// 练习 10.22

#include 
#include 
#include 
#include 
#include 
#include 
#include "make_plural.h"

using namespace std;
using namespace std::placeholders;  //_n定义在命名空间std::placeholders中
//检查单词长度,返回长度小于等于6的
bool check_size(const string & s, string::size_type sz)
{
	return s.size() <= sz;
}

//利用迭代器输出vector的内容
inline void output_words(vector & words)
{
	for (auto iter = words.begin(); iter != words.end(); ++iter)
	{
		cout << *iter << " ";
	}
	cout << endl;
}

void biggies(vector &words, vector::size_type sz)
{
	output_words(words);

	// 统计满足size() >= sz的元素的个数
	auto bc = count_if(words.begin(), words.end(), 
		bind(check_size, _1, sz));
	cout << bc << " " << make_plural(bc, "word", "s")
		<< " of length " << sz << " or longer" << endl;
}

int main(int argc, char * argv[])
{
	ifstream in(argv[1]);
	if (!in)
	{
		cout << "无法打开输入文件!" << endl;
		return -1;
	}

	vector words;
	string word;
	while (in >> word)
	{
		words.push_back(word);
	}

	biggies(words, 6);

	system("pause");
	return 0;
}

练习 10.23:bind 接受几个参数?

答:bind是可变参数的。它接受的第一个参数是一个可调用对象,即实际工作函数A,返回供算法使用的新的可调用对象B。若A接受x个参数,则bind的参数个数应该是x+1,即除了A外,其他参数应一一对应A所接受的参数。这些参数中有一部分来自于B(_n),另外一些来自于所处函数的局部变量。

练习 10.24:给定一个string,使用bind 和check_size 在一个int 的vector中查找第一个大于string 长度的值。

// 练习 10.24

#include 
#include 
#include 
#include 
#include 

using namespace std;
using namespace::placeholders;

//这里或许应该是int sz。
bool check_size(const string &s, string::size_type sz)
{
	return s.size() <= sz;
}

void biggies(vector & vc, const string & s)
{
	// 查找第一个大于等于s长度的数值
	//这里的_1表示的是每次对vector中int元素调用bind函数时传递进去的值。
	auto p = find_if(vc.begin(), vc.end(), bind(check_size, s, _1));
	// 打印结果
	cout << "第" << p - vc.begin() + 1 << "个数" << *p << "大于等于" << s << "的长度" << endl;
	cout << endl;
}

int main(int argc, char *argv[])
{
	vector vc = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
	biggies(vc, "hello");
	biggies(vc, "everyone");
	biggies(vc, "!");

	system("pause");
	return 0;
}

练习 10.25:在10.3.2节(第349 页)的练习中,编写了一个使用partition 的biggies版本。使用check_size 和bind 重写此函数。

// 练习 10.25

#include 
#include 
#include 
#include 
#include 
#include 

using namespace std;
using namespace std::placeholders;

string make_plural(size_t ctr, const string &word, const string &ending)
{
	return (ctr>1) ? word + ending : word;
}
//对vector进行排序并且删除重复的元素。
void elimdups(vector &words)
{
	sort(words.begin(), words.end());
	auto end_unique = unique(words.begin(), words.end());
	words.erase(end_unique, words.end());
}

bool check_size(const string &s, string::size_type sz)
{
	return s.size() >= sz;
}

void biggies(vector &words, vector::size_type sz)
{
	//打印words中的内容
	for_each(words.begin(), words.end(), [](const string &s){cout << s << " "; });
	cout << endl;
	
	elimdups(words);      //将words按字典序排序,删除重复词
	// 按长度排序,长度相同的单词维持字典序
	stable_sort(words.begin(), words.end(), [](const string &a, const string &b){return a.size()=sz的元素的后面
	auto wc = partition(words.begin(), words.end(), bind(check_size, _1, sz));
	// 计算满足size()>=sz的元素的数目
	auto count = wc - words.begin();
	cout << count << " " << make_plural(count, "word", "s") << " of length " << sz << " or longer" << endl;


	// 打印长度大于等于给定值的单词,每个单词后面接一个空格
	for_each(words.begin(), wc, [](const string &s){cout << s << " "; });
	cout << endl;
}

int main(int argc, char *argv[])
{
	ifstream in(argv[1]);
	if (!in){
		cout << "Open input file failed." << endl;
		return -1;
	}
	string str;
	vector vec;
	while (in >> str)
		vec.push_back(str);
	biggies(vec, 5);

	system("pause");
	return 0;
}

练习 10.26:解释三种插入迭代器的不同之处。

答:三者的差异在于如何向容器插入元素:back_inserter调用push_back,front_inserter 调用push_front,inserter则调用inserter。这决定了它们插入元素位置的不同。back_inserter总是插入到容器尾元素之后,front_inserter 总是插入到元素首元素之前,而inserter则是插入到给定位置之前。

练习 10.27:除了unique(参见10.2.3 节,第343 页)之外,标准库还定义了名为unique_copy 的函数, 它接受第三个迭代器,表示拷贝不重复元素的目的位置。编写一个程序,使用unique_copy 将一个vector 中不重复的元素拷贝到一个初始为空的list 中。

//练习 10.27
#include 
#include 
#include 
#include 
//迭代器定义的头文件
#include 

using namespace std;

int main()
{
	vector ivec = { 1, 2, 2, 3, 4, 5, 5, 6 };
	list ilist;
	//unique_copy接收第三个迭代器,表示拷贝不重复元素的目的位置。
	//unique_copy(ivec.begin(), ivec.end(), back_inserter(ilist));
	unique_copy(ivec.begin(), ivec.end(), inserter(ilist, ilist.begin()));

	for (auto v : ilist)
		cout << v << " ";
	cout << endl;

	system("pause");
	return 0;
}

练习 10.28:一个vector中保存1到9,将其拷贝到三个其他容器中。分别使用inserter 、back_inserter 和front_inserter 将元素添加到三个容器中。对每种inserter,估计输出序列是怎样的,运行程序验证你的估计是否正确。

//练习 10.28
#include 
#include 
#include 
#include 
#include 

using namespace std;

int main()
{
	vector ivec{ 1, 2, 3, 4, 5, 6, 7, 8, 9 };
	list ilist1, ilist2, ilist3;

	//三种方法将vector中的元素添加到list中。
	copy(ivec.begin(), ivec.end(), front_inserter(ilist1));
	copy(ivec.begin(), ivec.end(), inserter(ilist2, ilist2.begin()));
	copy(ivec.begin(), ivec.end(), back_inserter(ilist3));

	for (auto &r : ilist1)
		cout << r << " ";
	cout << endl;

	for (auto &r : ilist2)
		cout << r << " ";
	cout << endl;

	for (auto &r : ilist3)
		cout << r << " ";
	cout << endl;

	system("pause");
	return 0;
}

练习 10.29:编写程序, 使用流迭代器读取一个文本文件,存入一个vector 中的string里。

//练习 10.29
#include 
#include 
#include 
#include 
#include 

using namespace std;

int main(int argc, char *argv[])
{
	ifstream in(argv[1]);
	if (!in)
	{
		cout << "打开输入文件失败!" << endl;
		return -1;
	}

	//创建流迭代器从文件读入字符串
	istream_iterator in_iter(in);
	istream_iterator eof;
	vector words;
	while (in_iter != eof)
	{
		words.push_back(*in_iter++);
	}

	for (auto word : words)
		cout << word << " ";
	cout << endl;

	system("pause");
	return 0;
}

练习 10.30:使用流迭代器、sort 和copy 从标准输入读取一个整数序列,将其排序,并将结果写到标准输出。

//练习 10.30
#include 
#include 
#include 
#include 

using namespace std;

int main()
{
	//创建流迭代器从标准输入读入整数
	istream_iterator in_iter(cin);
	//尾后迭代器
	istream_iterator eof;
	vector ivec;
	
	while (in_iter != eof)
	{
		ivec.push_back(*in_iter++);
	}
	//sort排序
	sort(ivec.begin(), ivec.end());

	ostream_iterator out_iter(cout, " ");
	copy(ivec.begin(), ivec.end(), out_iter);


	system("pause");
	return 0;
}

练习 10.31:修改前一题的程序,使其只打印不重复的元素。你的程序应使用unique_copy (参见10.4.1节,第359 页) 。

//练习 10.31
#include 
#include 
#include 
#include 

using namespace std;

int main()
{
	//创建流迭代器从标准输入读取整数
	istream_iterator in_iter(cin);
	//尾后迭代器
	istream_iterator eof;
	vector ivec;

	while (in_iter != eof)
	{
		ivec.push_back(*in_iter++);
	}
	//排序
	sort(ivec.begin(), ivec.end());
	//输出流迭代器
	ostream_iterator out_iter(cout, " ");
	unique_copy(ivec.begin(), ivec.end(), out_iter);

	system("pause");
	return 0;
}

练习 10.32:重写1.6节(第21页)中的书店程序,使用一个vector 保存交易记录,使用不同算法完成处理。使用sort 和10.3.1节(第345 页)中的compareisbn 函数来排序交易记录,然后使用find和accumulate 求和。

待定。

练习 10.33:编写程序,接受三个参数: 一个输入文件和两个输出文件的文件名。输入文件保存的应该是整数。使用istream_iterator 读取输入文件。使用ostream_iterator 将奇数写入第一个输出文件,每个值之后都跟一个空格。将偶数写入第二个输出文件, 每个值都独占一行。

//练习 10.33
#include 
#include 
#include 
#include 

using namespace std;

int main(int argc, char *argv[])
{
	if (argc != 4)
	{
		cout << "用法:exercise.exe in_file out_file out file" << endl;
		return -1;
	}

	ifstream in(argv[1]);
	if (!in)
	{
		cout << "打开输入文件失败!" << endl;
		return -1;
	}

	ofstream out1(argv[2]);
	if (!out1)
	{
		cout << "打开输出文件1失败!" << endl;
		return -1;
	}

	ofstream out2(argv[3]);
	if (!out2)
	{
		cout << "打开输出文件2失败!" << endl;
		return -1;
	}

	//创建流迭代器从文件读入整数
	istream_iterator in_iter(in);
	//尾后迭代器
	istream_iterator eof;
	//第一个输出文件一空格间隔整数
	ostream_iterator out_iter1(out1, " ");
	//第二个输出文件以换行间隔整数
	ostream_iterator out_iter2(out2, "\n");

	while (in_iter != eof)
	{
		if (*in_iter & 1)
		{
			*out_iter1++ = *in_iter;   //这里的*和++没有什么实际意义,但是为了便于理解,还是写上。
		}
		else
		{
			*out_iter2++ = *in_iter;
		}
		++in_iter;
	}

	system("pause");
	return 0;
}

练习 10.34:使用reverse_iterator 逆序打印一个vector。

//练习 10.34
#include 
#include 
//#include 

using namespace std;

int main()
{
	vector ivec{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
	for (auto r_iter = ivec.crbegin(); r_iter != ivec.crend(); ++r_iter)
	{
		cout << *r_iter << " ";
	}
	cout << endl;

	system("pause");
	return 0;
}

练习 10.35:使用普通迭代器逆序打印一个vector。

//练习 10.35
#include 
#include 
#include 

using namespace std;

int main()
{
	vector ivec{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
	for (auto iter = ivec.end(); iter != ivec.begin(); )
	{
		cout << *(--iter) << " ";
	}
	cout << endl;

	system("pause");
	return 0;
}

练习 10.36:使用find 在一个int 的list 中查找最后一个值为0 的元素。

//练习 10.36
#include 
#include 
#include 

using namespace std;
//list是链表,元素不是连续存储的,迭代器不支持算术运算。
int main()
{
	list ilist{ 0, 1, 2, 0, 3, 4, 5, 0, 6 };
	auto last_z = find(ilist.rbegin(), ilist.rend(), 0);
	//将迭代器向链表头方向推进一个位置
	//转换为普通迭代器时,将回到最后一个0的位置
	//反向迭代器在转向普通迭代器的时候,会向末尾移动一个位置。
	last_z++;
	int p = 1; //用p来存储最后一个0出现的位置。
	//用base将last_z转换为普通迭代器
	//从链表头开始遍历,计数最后一个0的编号
	for (auto iter = ilist.begin(); iter != last_z.base(); iter++, p++);

	if (p >= ilist.size())
	{
		cout << "容器中没有0。" << endl;
	}
	else
	{
		cout << "最后一个0在第 " << p << "个位置" << endl;
	}

	system("pause");
	return 0;
}

练习 10.37:给定一个包含10 个元素的vector,将位置3 到7 之间的元素按逆序拷贝到一个list 中。

//练习 10.37
#include 
#include 
#include 
#include 
#include 

using namespace std;

int main(int argc, char *argv[])
{
	ostream_iterator out_iter(cout, " ");
	vector ivec{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
	//用流迭代器和copy输出int序列
	copy(ivec.begin(), ivec.end(), out_iter);
	cout << endl;

	list ilist;
	//将vi[2],也就是第3个元素的位置转换为反向迭代器
	vector::reverse_iterator r_end(ivec.begin() + 2);
	//将vi[7],也就是第8个元素的位置转换为反向迭代器
	vector::reverse_iterator r_begin(ivec.begin() + 7);
	//用反向迭代器将元素逆序拷贝至list
	copy(r_begin, r_end, back_inserter(ilist));

	//输出元素
	copy(ilist.begin(), ilist.end(), out_iter);
	cout << endl;

	system("pause");
	return 0;
}

练习 10.38:列出5 个迭代器类别,以及每类迭代器所支持的操作。

答:

输入迭代器:只读,不写;单遍扫描,只能递增;还支持相等性判定运算符(==、!=)、解引用运算符(*)和箭头运算符(->)。

输出迭代器:只写,不读;单遍扫描,只能递增,支持解引用运算符。

前向迭代器:可读写;多遍扫描,只能递增,支持所有输入、输出迭代器的操作。

双向迭代器:可读写;多遍扫描,可递增递减,支持所有前向迭代器操作。

随机访问迭代器:可读写,多遍扫描,支持全部迭代器运算。

练习 10.39:list 上的迭代器属于哪类?vector呢?

答:list上是双向迭代器,vector上是随机访问迭代器。

练习 10.40:你认为copy 要求哪类迭代器? reverse 和unique呢?

答:copy要求前两个参数至少是输入迭代器,表示一个输入范围。它读取这个范围中的元素,写入到第三个参数表示的输出序列中,因此第三个参数至少是输出迭代器。

reverse要反向处理序列,因此它要求两个参数至少是双向迭代器。

unique顺序扫描元素,覆盖重复元素,因此要求两个参数至少是前向迭代期,“至少”意味着能力更强的迭代器是可接受的。

练习 10.41:仅根据算法和参数的名字,描述下面每个标准库算法执行什么操作:

replace(beg, end, old_val, new_val);
//将范围[beg,end)间值等于old_val的元素替换为new_val。
replace_if(beg, end, pred, new_val);
//将范围[beg,end)间满足谓词pred的元素替换为new_val。
replace_copy(beg, end, dest, old_val, new_val);
//将范围[beg,end)间元素拷贝到目的序列dest中,将其中值等于old_val的元素替换为new_val。
replace_copy_if(beg, end, dest, pred, new_val);
//将范围[beg,end)间的元素拷贝到目的序列dest中,将其中满足谓词pred的元素替换为new_val。

练习 10.42:使用list代替vector 重新实现10.2.3 节(第343 页)中的去除重复单词的程序。

//练习 10.42
#include 
#include 
#include 
#include 

using namespace std;

inline void output_words(list &words)
{
	for (auto & r : words)
		cout << r << " ";
	cout << endl;
}

//list直接调用成员函数,而不是使用通用版本。
void elimDups(list &slst)
{
	output_words(slst);
	
	//按字典序排序,以便查找重复单词
	slst.sort();
	output_words(slst);
	//调用erase删除同一个值的连续拷贝
	slst.unique();
	output_words(slst);
}

int main()
{
	list slst{ "the", "quick", "red", "fox", "jumps", "over", "the", "slow", "red", "turtle" };

	elimDups(slst);

	system("pause");
	return  0;
}

 

你可能感兴趣的:(C++学习笔记)