泛型算法

algorithm頭文件中定義了大多數算法。

這些算法並不直接操作容器,而是遍歷由兩個迭代器指定的一個元素範圍,對其中的每個元素進行一些處理。

迭代器令算法不依賴於容器類型,但是算法依賴於元素類型的操作。大多數算法都使用了一個(或多個)元素類型上的操作,因此,除了默認操作符(==,<,>)外,我們還可以使用自定義的操作來代替默認操作。

注意:泛型算法本身不會執行容器的操作,它們只會運行於迭代器之上,執行迭代器的操作。

一、泛型算法分類

理解算法最基本的方法就是了解它們是否讀取元素、改變元素或是重排元素順序。

1、只讀算法

只會讀取其輸入範圍內的元素,而從不改變元素的算法稱爲只讀算法。

查找特定元素:

find(vec.cbegin(), vec.cend(), val);

數特定元素出現的次數:

count(vec.cbegin(), vec.cend(), val);

範圍內元素求和:

accumulate(vec.cbegin(), vec.cend(), 0);//第三個參數爲和的初值

比較兩個序列是否相等:

accumulate(vec.cbegin(), vec.cend(), 0);//第三個參數爲和的初值

2、寫容器元素的算法

寫容器元素算法可以將新值賦予序列中的元素。

給定首尾迭代器,向容器範圍內寫元素:

fill(vec.begin(), vec.end(), val);//將vec容器中所以元素置爲val

給定首迭代器和要寫入的元素數,向容器範圍內寫元素:

fill_n(vec.begin(), n, val);//將vec.begin()開始的n個元素置爲val

給定首尾迭代器,拷貝算法:

copy(roster1.cbegin(), roster1.cend(), roster2.begin());//將roster1中的所有元素拷貝到roster2.begin()開始的位置

給定首迭代器和要拷貝的元素數,拷貝算法:

copy_n(roster1.cbegin(), n, roster2.begin());//將roster1中roster1.cbegin()開始的n個元素拷貝到roster2.begin()開始的位置

替代算法:

replace(ilist.begin(), ilist.end(), 0, 42);//將ilist中所有的0元素替換爲42

替代拷貝算法:

replace_copy(ilist.cbegin(), lilist.cend(), back_inster(ivec), 0, 42);//在ivec中插入ilist的一份拷貝,但其中所有的0元素都被替換爲42

3、重排容器元素的算法

利用元素類型<運算符進行排序的算法:

清除相鄰重複項的算法:

將sort和unique結合使用可以實現序列清除重複項的排序。

二、定製操作

對於要比較元素的算法,默認使用的是<或==運算符。但在一些特殊場合,需要自己定義操作來代替默認運算符。

1、向算法傳遞函數——謂詞

謂詞是一個可調用的表達式,其返回結果是一個能用作條件的值。

例子:接受一個二元謂詞參數的sort版本用這個謂詞代替<來比較元素。我們可以自定義sort的排序規則,比如按元素長度進行排序。

首先定義一個函數,它輸入兩個string對象,返回一個布爾量來表示這兩個string對象的長度關係。

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

這個isShorter函數就可以作爲謂詞,在sort的一個3輸入參數的重載函數中使用。

sort(words.begin(), words.end(), isShorter);//由短到長排序words

2、lambda表達式

a)、問題的引出

帶謂詞的查找函數:

find_if(words.cbegin(), words.end(), isFinded);

由於find_if接受一元謂詞,也就是說我們傳遞給find_if的任何函數都必須嚴格接受一個參數,以便能用來自輸入序列的一個元素(而不是一個元素和一個長度)調用它。

但事實上,一個isFinded函數的實現需要兩個參數,一個string和一個長度。因此,isFinded函數不能成爲一元謂詞而在find_if函數中作爲判斷條件使用。

b)、使用lambda表達式

除了函數,任何“可調用對象”都可以作爲謂詞使用。對於一個對象或一個表達式,如果可以對其使用調用運算符,則稱其爲可調用的。

C++中共存在4種可調用對象:函數、函數指針、重載了函數調用運算符的類、lambda表達式。

一個lamdba表達式表示一個可調用的代碼單元,我們可以將其理解爲一個未命名的內聯函數。

形式:[capture list](paramater list) -> return type{function body}

其中,capture list是一個lambda所在函數中定義的局部變量的列表;paramater list是參數列表;return type表示返回值類型;function body是函數體。

注意:lambda必須使用尾置返回來指定返回類型。

我們可以忽略參數列表和返回類型,但必須永遠包括捕獲列表和函數體:

auto f = [] { return 42; };

c)、向lambda傳遞參數

與普通函數類似,lambda表達式的實參和形參必須匹配。

與普通函數不同,lambda表達式不能有默認參數,也就是說,一個lambda調用的實參數目永遠與形參數目相等。

例子:

與isShorter函數功能類似的lambda:

[](const string &a, const string &b)
{ return a.size() < b.size();}

我們可以在stable_sort中調用該lambda代替來isShorter函數:

stable_sort(words.begin(), word.end(),
[](const string &a, const string &b)
{ return a.size() < b.size();});

d)、使用捕獲列表

對於a)中提出的問題,怎樣構造一個表達式,可以將輸入序列中每個string長度與給定的長度進行比較?

事實上,我們可以使用捕獲列表將這個給定的長度(設爲sz)捕獲,作爲lambda表達式的一部分。

[sz](const string &a)
{return a.size() >= sz;};

使用此lambda,我們就可以查找第一個長度大於等於sz的元素:

auto wc = find_if(word.begin(), words.end(),
[sz] (const string &a)
{ return a.size() >= sz;});

注意:調用find_if的函數必須具有形參sz,否則lambda將無法捕獲sz,造成錯誤。



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章