謂詞
謂詞是一個可調用的表達式,其返回結果是一個能用作條件的值。
標準庫算法所用謂詞分爲兩類:
1、一元謂詞:意味着只接受單一參數 2、二元謂詞:意味着有兩個參數
接受謂詞的算法對序列的元素調用謂詞,因此元素類型必須能轉換爲謂詞的參數類型(並不要求類型一致)
比如下面這個函數就是一個二元謂詞,因爲有兩個參數。
bool isShorter(const string& s1, const string& s2)
{
return s1.size() < s2.size();
}
接受謂詞的排序算法可以更多樣化,我們可以自定義排序。
以下代碼是將重複元素刪去,然後將字符串按大小重排,相同長度的字符串按字典序排列。
#include<iostream>
#include<vector>
#include<string>
#include<algorithm>
using namespace std;
bool isShorter(const string& s1, const string& s2) //按長度排序
{
return s1.size() < s2.size();
}
template<typename T> void print(const T& seq) //模板輸出序列中的元素
{
for (auto i : seq)
cout << i << " ";
cout << endl;
}
void elimDups(vector<string>& v) //刪去重複元素
{
sort(v.begin(), v.end()); //先將字符串序列按字典序排列,重複的字符即排在一起
auto left = unique(v.begin(), v.end()); //unique算法“刪除”重複元素,返回不重複元素序列的下一個位置
v.erase(left, v.end()); //將不重複元素序列後的重複元素真正刪去
}
int main()
{
vector<string> v{ "the","quick","red","fox","jumps","over","the","slow","red","turtle" };
elimDups(v);
sort(v.begin(), v.end(),isShorter); //長度從小到大排列,相同長度的按字典序,stable_sort維持相等元素的原有順序,此處即字符串的字典序
print(v);
system("pause");
}
這個例子我們也可以注意到,unique算法只是將不重複的放在前面,返回不重複序列的下一位置,並沒真正刪除重複元素,要刪除還需容器erase操作刪除後面多餘元素。 因此我們可以確定, 算法不會直接添加/刪除元素,因爲其是對迭代器操作,若想真正添加/刪除元素要進行容器操作。
lambda表達式
前面說到,標準庫算法必須嚴格接受一個或者兩個參數的謂詞,但有時我們需要更多的參數,超出算法對謂詞的限制。比如我們寫一個程序求出一個字符串序列中大小大於給定長度的單詞有多少。這時會用到find_if算法,其接受一個一對迭代器和一個謂詞,返回第一個使謂詞非0的元素,若找不到則返回尾迭代器。我們寫一個函數,令其接受一個string和長度,返回一個bool表示是否大於給定長度,這十分容易,但find_if只接受一元謂詞,因此我們傳遞給find_if的函數都必須嚴格接受一個參數。這時就需要lambda表達式了。
lambda是一種可調用對象(即可被使用調用運算符()),一個lambda表達式表示一個可調用的代碼單元,我們可以將其理解爲一個未命名的內聯函數,但與函數不同,lambda可定義在函數內部。
形式:[capture list](parameter list) -> return type{function body}
使用時可以忽略參數列表和返回值,但必須永遠包括捕獲列表和函數體。忽略返回類型時,若函數體只有一句return語句,則返回類型會從中推斷出來,若有其他語句,則返回類型爲void。 捕獲列表中的變量是將在lambda函數體中使用的在lambda所在函數中定義的局部變量。有點繞……換句話說就是,一個lambda只有在其捕獲列表中捕獲它所在函數中的局部變量,才能在函數體中使用該變量。
以下是統計大小大於給定長度的單詞數量的完整程序代碼
#include<iostream>
#include<vector>
#include<string>
#include<algorithm>
using namespace std;
string make_plural(int i, const string& s1, const string& s2)
{
return i > 1 ? s1 + s2 : s1;
}
void elimDups(vector<string>& v) //刪除重複字符串
{
sort(v.begin(), v.end());
auto left_begin = unique(v.begin(), v.end());
v.erase(left_begin, v.end());
}
void biggies(vector<string>& v, vector<string>::size_type sz)
{
elimDups(v);
stable_sort(v.begin(), v.end(), //按長度排序,長度相同的按字典序
[](const string& s1, const string& s2) {return s1.size() < s2.size(); });
auto wc = find_if(v.begin(), v.end(), //找到第一個滿足長度>=sz的元素
[sz](const string& s) {return s.size() >= sz; });
auto count = v.end() - wc; //由於長度從小到大排好序了,由此可求出長度>=sz的元素個數
cout << count << " " << make_plural(count, "word", "s") << " of length " << sz << " or longer." << endl;
for_each(wc, v.end(), //輸出
[](const string& s) {cout << s << " "; });
cout << endl;
}
int main()
{
vector<string>v{ "the","quick","red","fox","jumps","over","the","slow","red","turtle" };
biggies(v, 5);
system("pause");
}
變量的捕獲方式:值捕獲、引用捕獲、隱式捕獲。
要注意的是,值捕獲中:與函數參數不同,被捕獲的變量是在lambda創建時拷貝,而非調用時拷貝。