c++ primer 第五版第十章

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

void test1001()
{
    int ia[] = {1, 2, 2, 4, 3, 51, 2};
    int num = 2;
    vector<int> ivec(ia, ia+7);
    int result = count(ivec.begin(), ivec.end(), num);
    cout << result << endl;
}

10.02 重做上一题,但读取string序列存入list中。

void test1002()
{
    string sa[] = {"1", "a", "abc", "abc", "abc", "bc", "abc"};
    string str = "abc";
    list<string> slst(sa, sa+7);
    int result = count(slst.begin(), slst.end(), str);
    cout << result << endl;
}

10.03 用accumulate求一个vector中的元素之和。

void test1003()
{
    int ia[] = {1, 2, 2, 4, 3, 51, 2};
    vector<int> ivec(ia, ia+7);

    int sum = accumulate(ivec.begin(), ivec.end(), 0);
    cout << sum << endl;
}

10.04 假定v是一个vector, 那么调用accumulate(v.cbegin(), v.cend(), 0)有何错误(如果存在)?

因为第三个参数是0,则编译器会认为其是一个int类型。最后的求和会损失精度。

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

c风格字符串有begin(), end()函数吗?不能使用equal,可以使用strcmp。

10.06 编写程序,使用fill_n将一个序列中的int都设置为0。

void test1006()
{
    vector<int> ivec(10);
    for (int i = 0; i < 10; i++) {
        ivec[i] = i+1;
    }

    fill_n(ivec.begin(), ivec.size(), 0);
}

10.07 下面程序是否有错误?如果有,请改正。

// a
vector<int> vec; list<int> lst; int i;
while (cin >> i)
    lst.push_back(i);
copy(lst.cbegin(), lst.cend(), vec.begin());
// 有错误,vec的大小为0,直接将lst复制到vec,将会发生不可预料的错误。
在copy前加上:vec.resize(lst.size());
// b
vector<int> vec;
vec.reserve(10);		// reserve是改变容器容量的,并不能改变容器大小。应改为resize。
fill_n(vec.begin(), 10, 0);

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

通过插入迭代器来插入元素,使用的是迭代器,而不是算法插入。

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

void elimDups(vector<string>& words)
{
    sort(words.begin(), words.end());   // 按字典序重排words

    for (auto i : words) {
        cout << i << " ";		// fox jumps over quick red red slow the the turtle
    }
    cout << endl;
    auto end_unique = unique(words.begin(), words.end());       // 重排,使每个单词只出现一次,指向不重复区域之后的迭代器。
    for (auto i : words) {
        cout << i << " ";		// fox jumps over quick red slow the turtle the red
    }
    cout << endl;
    words.erase(end_unique, words.end());
    for (auto i : words) {
        cout << i << " ";		// fox jumps over quick red slow the turtle
    }
    cout << endl;
}

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

标准库算法对迭代器而不是容器进行操作,因此,算法不能(直接)添加或删除元素。

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

void test1009()
{
    vector<string> svec = {"the", "quick", "red", "fox", "jumps", "over", "the", "slow", "red", "turtle"};
    elimDups(svec);
    printVec(svec);		// fox jumps over quick red slow the turtle
    // use isShorter
    sort(svec.begin(), svec.end(), isShorter);
    printVec(svec);		// fox red the over slow jumps quick turtle
    
    // use stable_sort
    stable_sort(svec.begin(), svec.end(), isShorter);
    printVec(svec);		// fox red the over slow jumps quick turtle
}

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

bool compareIsbn(const Sales_data& sale1, const Sales_data& sale2)
{
    return sale1.isbn().size() > sale2.isbn().size();
}
void test1012()
{
    vector<Sales_data> sale_vec;
    string bookNo = "";
    for (int i = 0; i < 5; i++) {
        bookNo += "s";
        cout << bookNo << endl;
        sale_vec.push_back(Sales_data(bookNo));
    }
    for (auto i : sale_vec) {
        print(cout, i);
    }
    sort(sale_vec.begin(), sale_vec.end(), compareIsbn);
    for (auto i : sale_vec) {
        print(cout, i);
    }
}

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

bool isStrLongThan5(string& str)
{
    return str.size() >= 5;
}

void test1013()
{
    vector<string> svec = {"hello", "world", "bin", "is", "the", "banana"};
    auto it = partition(svec.begin(), svec.end(), isStrLongThan5);
    printVec(svec);
    while (-- it != (svec.begin()-1)) {
        cout << *it << " ";
    }
}

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

auto sum = [](const int a, const int b) { return a+b; }

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

auto sum = [a](const int b) { return a+b; }

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

void biggies(vector<string>& words, size_t sz)
{
    elimDups(words);
    printVec(words);
    stable_sort(words.begin(), words.end(), [](const string& str1, const string& str2){ return (str1.size()>=str2.size());});
    find_if(words.begin(), words.end(), [sz](const string& str){ return str.size()>sz;});
    printVec(words);
}

void test1016()
{
    vector<string> svec = {"the", "quick", "red", "fox", "jumps", "over", "the", "slow", "red", "turtle"};
    biggies(svec, 4);
}

10.17 重新10.12的程序,在对sort的调用中使用lambda来代替函数compareIsbn。

sort(sale_vec.begin(), sale_vec.end(), [](const Sales_data& sale1, const Sales_data& sale2) { return sale1.isbn().size() > sale2.isbn().size(); });
// 将12题中的sort一行改为上述语句即可。

10.18 重写biggies,用partition代替find_if。我们在10.13中介绍了partition算法。

partition(words.begin(), words.end(), [sz](const string& str){ return str.size()>sz;});
// 将16题中的find_if一句改为上述数据即可。

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

stable_partition(words.begin(), words.end(), [sz](const string& str){ return str.size()>sz;});
// 将上题中的partition一行改为上述语句。

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

void test1020()
{
    int sz = 6;
    vector<string> svec = {"the", "quick", "red", "fox", "jumps", "over", "the", "slow", "red road", "turtle"};
    int cnt = count_if(svec.begin(), svec.end(), [sz](const string& str){ return str.size()>sz;});

    cout << cnt << endl;
}

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

void test1021()
{
    int val = 5;
    auto f = [&val]()->bool {
        if (0 == val) {
            return true;
        } else {
            -- val;
            return false;
        }
    };
    while (!f()) {
        cout << val << endl;
    }
}

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

bool isStrShorterThan6(string& str)
{
    return str.size() < 6;
}
void test1022()
{
    vector<string> svec = {"the", "quick", "red", "fox", "jumps", "over", "the", "slow", "red road", "turtle"};
    int cnt = count_if(svec.begin(), svec.end(), isStrShorterThan6);
    cout << cnt << endl;
}

10.23 bind接受几个参数?

auto newCallable = bind(callable, arg_list);

其中,arg_list是一个用逗号分隔的参数列表,对应给callable的参数。当我们调用newCallable时,实际会调用callable。

arg_list中的参数可能会包含形如_n的名字,其中n是一个整数。这些参数是“占位符”,表示newCallable的参数,它们占据了传递给newCallable的参数位置。

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

using namespace placeholders;
bool check_size(string& str, size_t sz)
{
    return str.size()>sz;
}
void test1024()
{
    vector<string> svec = {"the", "quick", "red", "fox", "jumps", "over", "the", "slow", "red road", "turtle"};
    auto iter = find_if(svec.begin(), svec.end(), bind(check_size, _1, 6));
    cout << *iter << endl;
}

10.25 在10.3.2节的练习中,编写了一个使用partition的biggies版本.使用check_size和bind重写此函数.

void biggies_25(vector<string>& words, size_t sz)
{
    printVec(words);
    auto iter = partition(words.begin(), words.end(), bind(check_size, _1, 6));
    printVec(words);
}

void test1025()
{
    vector<string> svec = {"the", "quick", "red", "fox", "jumps", "over", "the", "slow", "congratulate", "turtle"};
    biggies_25(svec, 5);
}

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

三种迭代器的区别在于元素插入的位置:

  1. back_inserter:创建一个使用push_back的迭代器。
  2. front_insert:创建一个使用push_front的迭代器。
  3. inserter:创建一个使用insert的迭代器。此函数接受第二个参数,元素将被插入到给定迭代器所表示的元素之前。

10.27 处理unique之外,标准库还定义了名为unique_copy的函数,它接受第三个迭代器,表示拷贝不重复元素的目的位置。编写一个程序,使用unique_copy将一个vector中不重复的元素拷贝到一个初始化为空的list中。

void test1027()
{
    vector<string> svec = {"the", "quick", "red", "fox", "jumps", "over", "the", "slow", "congratulate", "turtle"};
    list<string> slst(15);		// 注意list要分配空间,否则没有地方拷贝,程序core掉。

    unique_copy(svec.begin(), svec.end(), slst.begin());	
    for (auto i : slst) {
        cout << i << " ";
    }
    cout << endl;
}

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

void test1028()
{
    vector<int> ivec = {1, 2, 3, 4, 5, 6, 7, 8, 9};
    vector<int> ivec_dst1;
    list<int> ivec_dst2;		// 注意vector没有push_front方法,因此不能用front_inserter.list有该方法。
    vector<int> ivec_dst3;

    auto iter1 = ivec_dst1.begin();

    copy(ivec.begin(), ivec.end(), inserter(ivec_dst1, iter1));
    copy(ivec.cbegin(), ivec.cend(), front_inserter(ivec_dst2));
    copy(ivec.cbegin(), ivec.cend(), back_inserter(ivec_dst3));

    printContainer(ivec_dst1);
    printContainer(ivec_dst2);
    printContainer(ivec_dst3);
}

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

void readByFile(vector<string>& svec, string fileName)
{
    ifstream in(fileName);
    istream_iterator<string> in_iter(in);
    istream_iterator<string> eof;

    while (in_iter != eof) {
        svec.push_back(*in_iter++);
    }

    printContainer(svec);
}

void test1029()
{
    string fileName = "C:/Users/tutu/Documents/code/cpp_primer/ch10/string.txt";
    vector<string> svec;

    readByFile(svec, fileName);
}

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

void test1030()
{
    istream_iterator<int> in_iter(cin);
    istream_iterator<int> eof;
    vector<int> ivec;
    ostream_iterator<int> out_iter(cout, " ");

    while (in_iter != eof) {
        ivec.push_back(*in_iter++);
    }
    sort(ivec.begin(), ivec.end());
    copy(ivec.begin(), ivec.end(), out_iter);
    cout << endl;
}

10.31 修改前一题的程序,使其只打印不重复的元素。你的程序应使用unique_copy。

// 将上题中的copy一行改为:
unique_copy(ivec.begin(), ivec.end(), out_iter);

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

void test1031()
{
    Sales_item total;
    istream_iterator<Sales_item> in_iter(cin);
    istream_iterator<Sales_item> eof;
    vector<Sales_item> sale_vec;

    while (in_iter != eof) {
        sale_vec.push_back(*in_iter++);
    }

    sort(sale_vec.begin(), sale_vec.end(), compareIsbn);
    Sales_item sale_sum;
    for (auto beg = sale_vec.cbegin(), end = beg; beg != sale_vec.cend(); beg = end) {
        end = find_if(beg, sale_vec.cend(), [beg](const Sales_item &item){ return item.isbn() != beg->isbn(); });
        cout << accumulate(beg, end, Sales_item(beg->isbn())) << endl;
    }
}
// Sales_item类中需要重载"<<" ">>" "+" "+="等符号,才能支持accumulate函数的运算。

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

void readIntByFile(string& in_file, string& out_file_odd, string& out_file_even)
{
    ifstream in(in_file);
    ofstream out_odd(out_file_odd);
    ofstream out_even(out_file_even);
    istream_iterator<int> in_iter(in), eof;
    ostream_iterator<int> out_iter_odd(out_odd, " "), out_iter_even(out_even, "\n");

    std::for_each(in_iter, eof, [&out_iter_odd, &out_iter_even](const int i) {
             (i%2 ? out_iter_odd : out_iter_even)++ = i;
             });
}

void test1033()
{
    string inFileName = "C:/Users/tutu/Documents/code/cpp_primer/ch10/int.txt";
    string outFileOdd = "C:/Users/tutu/Documents/code/cpp_primer/ch10/odd.txt";
    string outFileEven = "C:/Users/tutu/Documents/code/cpp_primer/ch10/even.txt";

    readIntByFile(inFileName, outFileOdd, outFileEven);
}

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

void test1034()
{
    vector<int> ivec = {0, 1, 2, 3, 4, 5, 6};

    for (auto riter = ivec.crbegin(); riter != ivec.crend(); ++ riter) {
        cout << *riter << " ";
    }
    cout << endl;
}

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

    auto iter = ivec.cend();
    while (iter != ivec.cbegin()) {
        cout << *--iter << " ";
    }
    cout << endl;

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

void test1035()
{
    list<int> ilst = {0, 1, 2, 3, 4, 5, 6, 0, 1, 3};
    auto find_iter = find (ilst.crbegin(), ilst.crend(), 0);
    int dis = distance(find_iter, ilst.crend());
    cout << dis << endl;
}

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

    vector<int> ivec = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
    list<int> dst_lst(10);
    copy(ivec.cbegin()+3, ivec.cbegin()+8, dst_lst.rbegin());
    for (auto i : dst_lst) {
        cout << i << " ";
    }
    cout << endl;

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

  1. 输入迭代器:只读,不写;单遍扫描,只能递增。支持==、!=、++、*、->
  2. 输出迭代器:只写,不读;单遍扫描,只能递增。支持++、*
  3. 前向迭代器:可读写,多边扫描,只能递增。支持输入输出迭代器的所有操作。
  4. 双向迭代器:可读写,多遍扫描,可递增递减。除支持前向迭代器的操作外,还支持--。
  5. 随机访问迭代器:可读写,多遍扫描,支持全部迭代器运算。除支持双向操作外,还支持<、<=、>、>=、+、+=、-、-=、迭代器的减法运算符(-)、下标运算符(iter[n], *(iter[n]) )。

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

list的迭代器属于双向迭代器。

vector属于随机访问迭代器。

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

copy前两个参数要求输入迭代器,最后一个参数要求输出迭代器。

reverse要求双向迭代器。

unique要求前向迭代器。

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

replace(beg, end, old_val, new_val);
replace_if(beg, end, pred, new_val);
replace_copy(beg, end, dest, old_val, new_val);
replace_copy_if(beg, end, dest, pred, new_val);

10.42 使用list代替vector重新实现10.2.3节中的去除重复单词的程序。

void test1042()
{
    list<int> ilst = {0, 1, 1,2, 3, 4, 5, 6, 0, 1, 3};
    ilst.sort();
    ilst.unique();

    for (auto i : ilst) {
        cout << i << " ";
    }
    cout << endl;
}
posted @ 2018-08-28 11:45  安月月  阅读(1664)  评论(0编辑  收藏  举报