C++拾取——使用STD標準庫簡化程式碼
關係數列
等差數列
比如我們要構建的序列儲存的值是1,2,3,4……10000。
常規寫法
使用for迴圈
std::vector<int> vec; for (int i = 0; i < 10000; i++) { vec.push_back(i + 1); }
簡潔寫法
使用std的標準庫iota。
std::vector<int> vec(10); std::iota(vec.begin(), vec.end(), 1);
使用std標準庫的partial_sum,程式碼量減少了一半。 partial是區域性、區間的意思,sum是累加的意思。第1、2個引數是需要被計算的容器起止迭代器,第3個引數是計算結果儲存的起始迭代器。它還有第4個引數,用於描述怎麼計算的。
std::vector<int> vec(10000, 1); std::partial_sum(vec.begin(), vec.end(), vec.begin());
std::partial_sum方法對區間資料進行累加。具體的計算規則是
template< class InputIt, class OutputIt > OutputIt partial_sum( InputIt first, InputIt last, OutputIt d_first ); // *(d_first)= *first; // *(d_first+1) = *first + *(first+1); // *(d_first+2) = *first + *(first+1) + *(first+2); // *(d_first+3) = *first + *(first+1) + *(first+2) + *(first+3); // ...
上述方法有個缺點,就是需要填充10000個1之後再計算。我們可以稍微修改如下,效率會好些。
std::vector<int> vec(10000); vec[0] = 1; std::partial_sum(vec.begin(), vec.end(), vec.begin(), [](const int&a, int b) {return a + 1;});
如果要生成10000,9999,9998……1這樣遞減的數列,則可以把第一個元素賦值為10000後,傳遞一個減法lambda表示式
std::vector<int> vec(10000); vec[0] = 10000; std::partial_sum(vec.begin(), vec.end(), vec.begin(), [](const int&a, int b) {return a - 1;} );
等比數列
比如我們要生成1,2,4,8,16……這樣2倍關係的數列。
常規寫法
std::vector<int> vec; for (int i = 0; i < 10; i++) { vec.push_back(pow(2, i)); }
精簡寫法
std::vector<int> vec(10, 2); vec[0] = 1; std::partial_sum(vec.begin(), vec.end(), vec.begin(), std::multiplies<int>());
使用比值2初始化vector容器,然後給partial_sum傳遞乘法函式物件。
或者使用lambda表示式
std::vector<int> vec(10); vec[0] = 1; std::partial_sum(vec.begin(), vec.end(), vec.begin(), [](const int& x, int y) {return x * 2;});
如果要生成512,256……2,1這樣的等比數列,則可以把容器的第一個元素設定為1024,然後給partial_sum傳遞除法函式物件。
std::vector<int> vec(10, 2); vec[0] = 512; std::partial_sum(vec.begin(), vec.end(), vec.begin(), std::divides<int>());
或者使用lambda表示式
std::vector<int> vec(10); vec[0] = 512; std::partial_sum(vec.begin(), vec.end(), vec.begin(), [](const int& x, int y) {return x / 2;});
斐波那契數列
常規寫法
std::vector<int> vec(10); vec[0] = 1; for (auto it = std::next(vec.begin()); it != vec.end(); it++) { auto it_prev = std::prev(it); if (vec.begin() != it_prev) { *it = *it_prev + *std::prev(it_prev); } else { *it = *it_prev; } }
精簡寫法
std::vector<int> vec(10); vec[0] = 1; std::adjacent_difference(vec.begin(), std::prev(vec.end()), std::next(vec.begin()), std::plus<int>());
adjacent_difference用於計算前後兩個資料的差。第4個引數的預設操作是減法,其計算規則如下
template< class ExecutionPolicy, class ForwardIt1, class ForwardIt2 > ForwardIt2 adjacent_difference( ExecutionPolicy&& policy, ForwardIt1 first, ForwardIt1 last, ForwardIt2 d_first ); // *(d_first)= *first; // *(d_first+1) = *(first+1) - *(first); // *(d_first+2) = *(first+2) - *(first+1); // *(d_first+3) = *(first+3) - *(first+2); // ...
累計型操作
比較常見的累計型操作如累加、累乘
累加
常規寫法
std::vector<int> vec = { 16, 8, 4 }; int sum = 0; for (int n : vec) { sum += n; }
精簡寫法
std::vector<int> vec = { 16, 8, 4 }; int sum = std::accumulate(vec.begin(), vec.end(), 0);
程式碼也減少一半。
accumulate第1、2個引數是需要計算的容器的起止迭代器,第3個引數是初始計算的值。它還有第4個引數,用於描述如何累計。預設是累加操作。
我們再看個累乘操作。
std::vector<int> vec = { 16, 8, 4 }; int product = std::accumulate(vec.begin(), vec.end(), 1, std::multiplies<int>());
組合成一個字串
常規寫法
std::vector<int> vec = { 16, 8, 4 }; std::string str; for (int n : vec) { if (!str.empty()) { str.append(","); } str.append(std::to_string(n)); }
精簡寫法
std::string s = std::accumulate(std::next(vec.begin()), vec.end(), std::to_string(vec[0]), // start with first element [](std::string a, int b) { return a + ',' + std::to_string(b);} );
序列比較
兩個序列中,相同偏移的元素值相等的個數
常規寫法
std::vector<int> a = { 1, 2, 3, 4, 5 }; std::vector<int> b = { 5, 4, 3, 2, 1 }; int num = 0; auto it_a = a.begin(); auto it_b = b.begin(); for (; it_a != a.end() && it_b != b.end(); it_a++, it_b++) { if (*it_a == *it_b) { num++; } }
精簡寫法
std::vector<int> a = { 1, 2, 3, 4, 5 }; std::vector<int> b = { 5, 4, 3, 2, 1 }; int num = std::inner_product(a.begin(), a.end(), b.begin(), 0, std::plus<>(), std::equal_to<>());
inner_product方法對兩個序列中相同位置的元素使用第5個引數指向的函式物件計算,計算的結果通過第4個引數指向的函式物件進行再計算。即
template<class InputIt1, class InputIt2, class T, class BinaryOperation1, class BinaryOperation2> T inner_product( InputIt1 first1, InputIt1 last1, InputIt2 first2, T init, BinaryOperation1 op1, BinaryOperation2 op2 ); // 以初值 init 初始化積累器 acc ,然後 // 以表示式 acc = op1(acc, op2(*first1, *first2)) 修改它,再以表示式acc = op1(acc, op2(*(first1+1), *(first2+1))) 修改它,以此類推
兩個序列元素是否完全一致(順序無關)
比如一個序列a是1,2,3;序列b是2,1,3;序列c是1,2,1。則a和b中元素完全一致,只是順序不一致;而c和a、b中元素不一致。可以想象這個演算法不是簡簡單單就能寫出來的。我們直接看精簡寫法
精簡寫法
std::vector<int> v1{ 1,1,2,2,5 }; std::vector<int> v2{ 5,1,2,1,2 }; bool permutation = std::is_permutation(v1.begin(), v1.end(), v2.begin(), v2.end());
is_permutation用於判斷兩個序列是否是同一個序列的不同(或相同)順序排列。