C++ primer 第十章習題

chapter10 泛型算法

練習

10.1 節練習

練習10.1

  • 頭文件algorithm中定義了一個名爲count的函數,它類似find,接受一對迭代器和一個值作爲參數。count返回給定值在序列中出現的次數。編寫程序,讀取int序列存入vector中,打印有多少個元素的值等於給定值。
#include <algorithm>
int main() {
	int i;
	vector<int> vi;
	while (cin >> i)
		vi.push_back(i);
	cout << count(vi.cbegin(), vi.cend(), 6);
}

練習10.2

  • 重做上一題,但讀取string序列存入 list 中。
#include <algorithm>
int main() {
	string str;
	list<string> lst;
	while (cin >> str)
		lst.push_back(str);
	cout << count(lst.cbegin(), lst.cend(), "a");
}

10.2.1 節練習

練習10.3

  • 用accumulate求一個vector中的元素之和。
#include <numeric>
int main(){
    vector<int> vi = {1,2,3,4,5,6,7,8,9,10};
    cout << accumulate(vi.cbegin(), vi.cend(), 0) << endl;
}

練習10.4

  • 假定v是一個vector,那麼調用 accumulate(v.cbegin(),v.cend(),0) 有何錯誤(如果存在的話)?

最終得到的結果將會由於每次將值添加到結果時,因double到int的隱式轉換丟失精度。

練習10.5

  • 在本節對名冊(roster)調用equal 的例子中,如果兩個名冊中保存的都是C風格字符串而不是string,會發生什麼?

正常運行並返回正確結果。

10.2.2 節練習

練習10.6

  • 編寫程序,使用fill_n將一個序列中的 int 值都設置爲 0。
int main(){
    vector<int> vi(10, 10);
    fill_n(vi.begin(), vi.size(), 0);
    for (int i : vi)
        cout << i << endl;
}

練習10.7

  • 下面程序是否有錯誤?如果有,請改正:
(a) 
vector<int> vec; list<int> lst; int i;
while (cin >> i)
    lst.push_back(i);
copy(lst.cbegin(), lst.cend(), vec.begin());
(b)
vector<int> vec;
vec.reserve(10);
fill_n(vec.begin(), 10, 0);

(a)有錯。vec是空容器,無法向一個空容器去複製數據。

(b)有錯。雖然爲vec提供了內存空間,但此時vec仍是一個空容器,無法向空容器去寫入元素,該語句的結果是未定義的。

練習10.8

  • 本節提到過,標準庫算法不會改變它們所操作的容器的大小。爲什麼使用back_inserter不會使這一斷言失效?

對於標準庫算法而言,它只會使用賦值算法將容器內的值改變。而當標準庫算法使用back_inserter時,賦值運算符將變作調用push_back函數,而非原本的賦值函數。可見這一行爲是因爲使用了back_inserter才導致改變了容器的大小,對於標準庫算法而言僅僅是調用了賦值算法而已,因此不會使這一斷言失效。

10.2.3 節練習

練習10.9

  • 實現你自己的elimDups。測試你的程序,分別在讀取輸入後、調用 unique後以及調用erase後打印vector的內容。
void eliminate_duplicates(vector<string> &v)
{
    sort(v.begin(), v.end());
    for (auto i : v)
        cout << i << "  ";
    cout << endl;

    auto end_unique = unique(v.begin(), v.end());
    for (auto i : v)
        cout << i << "  ";
    cout << endl;

    v.erase(end_unique, v.end());
    for (auto i : v)
        cout << i << "  ";
    cout << endl;
}
int main()
{
    vector<string> v = {"abc", "abc", "efg", "hij", "klmn", "opq", "xyz", "klmn"};
    eliminate_duplicates(v);
    return 0;
}

練習10.10

  • 你認爲算法不改變容器大小的原因是什麼?

因爲泛型函數使用的是迭代器,因此其本身就沒有容器的概念。這也是爲了通用性服務,不考慮容器本身,而通過迭代器來直接訪問其中的元素。另外,由於迭代器容易因爲元素的刪除插入等原因失效,因此爲了避免這種情況發生,會限制泛型函數進行增刪的操作,避免出現迭代器失效的情況。

10.3.1 節練習

練習10.11

  • 編寫程序,使用stable_sort和isShorter將傳遞給你的elimDups版本的vector排序。打印vector的內容,驗證你的程序的正確性。
void eliminate_duplicates(vector<string> &v)
{
	sort(v.begin(), v.end());
	auto end_unique = unique(v.begin(), v.end());
	v.erase(end_unique, v.end());
}

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

int main()
{
	vector<string> v = { "abc", "abc", "efg", "hij", "klmn", "opq", "xyz", "klmn" };
	eliminate_duplicates(v);
	stable_sort(v.begin(), v.end(), isShorter);
	for (auto i : v)
		cout << i << " ";
	return 0;
}

練習10.12

  • 編寫名爲compareIsbn的函數,比較兩個 Sales_data 對象的isbn( ) 成員。使用這個函數排序一個保存 Sales_data 對象的 vector。
void compareIsbn(const Sales_data &S1, const Sales_data &S2){
    return S1.isbn() < S2.isbn();
}

練習10.13

  • 標準庫定義了名爲 partition 的算法,它接受一個謂詞,對容器內容進行劃分,使得謂詞爲true 的值會排在容器的前半部分,而使得謂詞爲 false 的值會排在後半部分。算法返回一個迭代器,指向最後一個使謂詞爲 true 的元素之後的位置。編寫函數,接受一個 string,返回一個 bool 值,指出 string 是否有5個或更多字符。使用此函數劃分 words。打印出長度大於等於5的元素。
bool morethan5(const string &str) {
	return str.size() > 4;
}
int main() {
	vector<string> v{ "1", "12", "123", "1234", "12345", "123456" };
	for (const string &str : v) {
		if (morethan5(str))
			cout << str << endl;
	}
}

10.3.2 節練習

練習10.14

  • 編寫一個 lambda ,接受兩個int,返回它們的和。
int main() {
	auto f = [](int i, int j) {return i + j; };
	cout << f(1, 2) << endl;
}

練習10.15

  • 編寫一個 lambda ,捕獲它所在函數的 int,並接受一個 int參數。lambda 應該返回捕獲的 int 和 int 參數的和。
int main() {
	int i = 5;
	auto f = [i](int j) {return i + j; };
	cout << f(5);
}

練習10.16

  • 使用 lambda 編寫你自己版本的 biggies。

略。

練習10.17

  • 重寫10.3.1節練習10.12(第345頁)的程序,在對sort的調用中使用 lambda 來代替函數 compareIsbn。
sort(v.begin(), v.end(), [](const Sales_data &s1, const Sales_data &s2) 
     {return s1.isbn() < s2.isbn(); });

練習10.18

  • 重寫 biggies,用 partition 代替 find_if。我們在10.3.1節練習10.13(第345頁)中介紹了 partition 算法。
void eliminate_duplicates(vector<string> &v)
{
	sort(v.begin(), v.end());
	auto end_unique = unique(v.begin(), v.end());
	v.erase(end_unique, v.end());
}

void biggies(vector<string> &words, vector<string>::size_type sz) {
	eliminate_duplicates(words);
	stable_sort(words.begin(), words.end(), [](const string &a, const string &b) {return a.size() < b.size(); });
	auto wz = partition(words.begin(), words.end(), [sz](const string &a) {return a.size() < sz; });
	auto count = words.end() - wz;
	for_each(wz, words.end(), [](const string &s) { cout << s << " "; });
}

int main() {
	std::vector<std::string> v = { "a", "b", "bc", "abc", "1234", "12345", "123456" };
	biggies(v, 4);
}

練習10.19

  • 用 stable_partition 重寫前一題的程序,與 stable_sort 類似,在劃分後的序列中維持原有元素的順序。
void eliminate_duplicates(vector<string> &v)
{
	sort(v.begin(), v.end());
	auto end_unique = unique(v.begin(), v.end());
	v.erase(end_unique, v.end());
}

void biggies(vector<string> &words, vector<string>::size_type sz) {
	eliminate_duplicates(words);
	stable_sort(words.begin(), words.end(), [](const string &a, const string &b) {return a.size() < b.size(); });
	auto wz = stable_partition(words.begin(), words.end(), [sz](const string &a) {return a.size() >= sz; });
	auto count = words.end() - wz;
	for_each(wz, words.end(), [](const string &s) { cout << s << " "; });
}

int main() {
	std::vector<std::string> v = { "13", "12", "123", "1234", "12345", "123456" };
	biggies(v, 4);
}

10.3.3 節練習

練習10.20

  • 標準庫定義了一個名爲 count_if 的算法。類似 find_if,此函數接受一對迭代器,表示一個輸入範圍,還接受一個謂詞,會對輸入範圍中每個元素執行。count_if返回一個計數值,表示謂詞有多少次爲真。使用count_if重寫我們程序中統計有多少單詞長度超過6的部分。
void eliminate_duplicates(vector<string> &v)
{
	sort(v.begin(), v.end());
	auto end_unique = unique(v.begin(), v.end());
	v.erase(end_unique, v.end());
}

int biggies(vector<string> &words, vector<string>::size_type sz) {
	eliminate_duplicates(words);
	auto count = count_if(words.begin(), words.end(), [sz](const string &a) {return a.size() >= sz; });
	return count;
}

int main() {
	std::vector<std::string> v = { "13", "12", "123", "1234", "12345", "123456" };
	cout << biggies(v, 4);
}

練習10.21

  • 編寫一個lambda,捕獲一個局部Int變量,並遞減變量值,直至它變爲0。一旦變量變爲0,再調用lambda應該不再遞減變量。lambda應該返回一個bool值,指出捕獲 的變量是否爲0。
int main() {
	int i = 10;
	auto f = [&i]() {if (i == 0) return true; else { while (i != 0) --i; return false; }};
	cout << f() << "   " << i << endl;
	cout << f() << "   " << i << endl;
}

10.3.4 節練習

練習10.22

  • 重寫統計長度小於等於6的單詞數量的程序,使用函數代替 lambda。
void eliminate_duplicates(vector<string> &v)
{
	sort(v.begin(), v.end());
	auto end_unique = unique(v.begin(), v.end());
	v.erase(end_unique, v.end());
}
bool check_size(const string &str, string::size_type sz) {
	return str.size() >= sz;
}
int biggies(vector<string> &words, vector<string>::size_type sz) {
	eliminate_duplicates(words);
	auto count = count_if(words.begin(), words.end(), bind(check_size, _1, sz));
	return count;
}

int main() {
	std::vector<std::string> v = { "13", "12", "123", "1234", "12345", "123456" };
	cout << biggies(v, 4);
}

練習10.23

  • bind 接受幾個參數?

bind函數接收一個函數,並接收一個參數列表,其中參數列表的數量沒有限制。

練習10.24

  • 給定一個string,使用 bind 和 check_size 在一個 int 的vector 中查找第一個大於string長度的值。
bool check_size(const string &str, string::size_type sz) {
	return str.size() < sz;
}
int main(){
    string str = "hello";
    vector<int> vi = {1,2,3,4,5,6,7,8,9};
    find_if(vi.begin(), vi.end(), bind(check_size, str, _1));
}

練習10.25

  • 在10.3.2節(第349頁)的練習中,編寫了一個使用partition 的biggies版本。使用 check_size 和 bind 重寫此函數。
void eliminate_duplicates(vector<string> &v)
{
	sort(v.begin(), v.end());
	auto end_unique = unique(v.begin(), v.end());
	v.erase(end_unique, v.end());
}
bool check_size(const string &str, string::size_type sz) {
	return str.size() < sz;
}
void biggies(vector<string> &words, vector<string>::size_type sz) {
	eliminate_duplicates(words);
	stable_sort(words.begin(), words.end(), [](const string &a, const string &b) {return a.size() < b.size(); });
	auto wz = partition(words.begin(), words.end(), bind(check_size, _1, sz));
	auto count = words.end() - wz;
	for_each(wz, words.end(), [](const string &s) { cout << s << " "; });
}

int main() {
	std::vector<std::string> v = { "a", "b", "bc", "abc", "1234", "12345", "123456" };
	biggies(v, 4);
}

10.4.1 節練習

練習10.26

  • 解釋三種插入迭代器的不同之處。

back_inserter 創建一個使用push_back的迭代器,將元素插入的容器末尾。接收被插入元素一個參數。

front_inserter 創建一個使用push_front的迭代器,將元素插入到容器頭部。接收被插入元素一個參數。

inserter 創建一個使用insert的迭代器,將元素插入到指定位置。接收被插入元素和目標位置兩個參數。

練習10.27

  • 除了 unique(參見10.2.3節,第343頁) 之外,標準庫還定義了名爲 unique_copy 的函數,它接受第三個迭代器,表示拷貝不重複元素的目的位置。編寫一個程序,使用 unique_copy將一個vector中不重複的元素拷貝到一個初始化爲空的list中。
int main() {
	vector<int> v1 = { 1,1,2,2,3,3,4,4,5,5 };
	vector<int> v2;
	unique_copy(v1.begin(), v1.end(), back_inserter(v2));
	for (int i : v2)
		cout << i << endl;
}

練習10.28

  • 一個vector 中保存 1 到 9,將其拷貝到三個其他容器中。分別使用inserter、back_inserter 和 front_inserter 將元素添加到三個容器中。對每種 inserter,估計輸出序列是怎樣的,運行程序驗證你的估計是否正確。
int main() {
	vector<int> v1 = { 1,2,3,4,5,6,7,8,9 };
	list<int> lst1;
	list<int> lst2;
	list<int> lst3;
	copy(v1.begin(), v1.end(), inserter(lst1, lst1.begin()));
	for (int i : lst1)    //{1,2,3,4,5,6,7,8,9}
		cout << i;
	cout << endl;
	copy(v1.begin(), v1.end(), back_inserter(lst2));
	for (int i : lst2)    //{1,2,3,4,5,6,7,8,9}
		cout << i;
	cout << endl;
	copy(v1.begin(), v1.end(), front_inserter(lst3));
	for (int i : lst3)    //{9,8,7,6,5,4,3,2,1}
		cout << i;
}

10.4.2 節練習

練習10.29

  • 編寫程序,使用流迭代器讀取一個文本文件,存入一個vector中的string裏。
int main(){
    string str = "file_name";
    ifstream input(stf);
    istream_iterator<string> iter(input), eof;
    vector<string> v(iter(input), eof);
    ostream_iterator<string> iter_out(cout, "\n");
    copy(v.begin(), v.end(), iter_out);
    cout << endl;
}

練習10.30

  • 使用流迭代器、sort 和 copy 從標準輸入讀取一個整數序列,將其排序,並將結果寫到標準輸出。
int main() {
	istream_iterator<int> in(cin), eof;
	vector<int> v(in, eof);
	sort(v.begin(), v.end());
	ostream_iterator<int> out(cout, "\n");
	copy(v.begin(), v.end(), out);
	cout << endl;
}

練習10.31

  • 修改前一題的程序,使其只打印不重複的元素。你的程序應該使用 unique_copy(參見10.4.1節,第359頁)。
int main() {
	istream_iterator<int> in(cin), eof;
	vector<int> v(in, eof);
	sort(v.begin(), v.end());
	ostream_iterator<int> out(cout, "\n");
	unique_copy(v.begin(), v.end(), out);
	cout << endl;
}

練習10.32

  • 重寫1.6節(第21頁)中的書店程序,使用一個vector保存交易記錄,使用不同算法完成處理。使用 sort 和10.3.1節(第345頁)中的 compareIsbn 函數來排序交易記錄,然後使用 find 和 accumulate 求和。

練習10.33

  • 編寫程序,接受三個參數:一個輸入文件和兩個輸出文件的文件名。輸入文件保存的應該是整數。使用 istream_iterator 讀取輸入文件。使用 ostream_iterator 將奇數寫入第一個輸入文件,每個值後面都跟一個空格。將偶數寫入第二個輸出文件,每個值都獨佔一行。
void func(const string& input_filename, const string& output_filename1, const string& output_filename2){
    ifstream in(input_filename);
    istream_iterator<int> iter_in(in), eof;
    ostream_iterator<int> iter_out1(ofstream(output_filename1), " "), ite_out2(ofsteam(out_filename2), "\n");
    while(iter_in != eof){
        if (i&1 == 1)
            *iter_out1++ = i;
        else
            *iter_out2++ = i;
        ++iter_in;
    }
}

10.4.3 節練習

練習10.34

  • 使用 reverse_iterator 逆序打印一個vector。
int main() {
	vector<int> v = { 1,2,3,4,5,6,7,8 };
	for (auto riter = v.crbegin(); riter != v.crend(); ++riter)
		cout << *riter << endl;
}

練習10.35

  • 使用普通迭代器逆序打印一個vector。
int main() {
	vector<int> v = { 1,2,3,4,5,6,7,8 };
	for (auto iter = v.cend() - 1; iter >= v.cbegin(); --iter) {
		cout << *iter << endl;
		if (iter == v.cbegin())
			break;
	}
}

練習10.36

  • 使用 find 在一個 int 的list 中查找最後一個值爲0的元素。
int main() {
	list<int> lst = { 1,0,1,0,1,0,1 };
	auto index = find(lst.crbegin(), lst.crend(), 0);
	cout << "last zero: " << *index << endl;
}

練習10.37

  • 給定一個包含10 個元素的vector,將位置3到7之間的元素按逆序拷貝到一個list中。
int main() {
	vector<int> vi = { 0,1,2,3,4,5,6,7,8,9 };
	list<int> lst;
	copy(vi.crbegin() + 2, vi.crend() - 3, back_inserter(lst));
	for (int i : lst)
		cout << i << endl;
}

10.5.1 節練習

練習10.38

  • 列出5個迭代器類別,以及每類迭代器所支持的操作。

輸入迭代器(input iterator): iter1==iter2 、iter1!=iter2 、 iter++、 *iter 、 iter->xx

輸出迭代器(output iterator): ++iter、 *iter

前向迭代器(forward iterator): iter1==iter2 、iter1!=iter2 、 iter++、 *iter 、 iter->xx

雙向迭代器(bidirectional iterator): iter1==iter2 、iter1!=iter2 、 iter++、 iter-- 、*iter 、 iter->xx

隨機訪問迭代器(random-access iterator):iter1==iter2 、iter1!=iter2、iter++、iter-- 、*iter 、 iter->xx 、 iter1(< 、 <= 、 > 、 >=)iter2、 iter(+、+=、-、-=)i 、iter1-iter2、iter[n]

練習10.39

  • list 上的迭代器屬於哪類?vector呢?

list:前向迭代器。vector:隨機訪問迭代器。

練習10.40

  • 你認爲 copy 要求哪類迭代器?reverse 和 unique 呢?

copy:前兩個爲輸入,後一個爲輸出。

reverse:雙向迭代器。

unique:前向迭代器。

10.5.3 節練習

練習10.41

  • 僅根據算法和參數的名字,描述下面每個標準庫算法執行什麼操作:

replace(beg, end, old_val, new_val);

begend範圍內所有值爲old_val的元素替換爲new_val

replace_if(beg, end, pred, new_val);

begend範圍內所有通過謂詞pred判定爲真的元素替換爲new_val

replace_copy(beg, end, dest, old_val, new_val);

begend範圍內所有值爲old_val的元素替換爲new_val的結果複製到dest迭代器及其後續部分。不改變原容器內的值。

replace_copy_if(beg, end, dest, pred, new_val);

begend範圍內所有通過謂詞pred判定爲真的元素替換爲new_val的結果複製到dest迭代器及其後續部分。不改變原容器內的值。

10.6 節練習

練習10.42

  • 使用 list 代替 vector 重新實現10.2.3節中的去除重複單詞的程序。
void elimDups(list<string> &words){
    sort(words.begin(), words.end());
    list.unique();
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章