1. 程式人生 > >C/C++基礎----泛型算法

C/C++基礎----泛型算法

for_each 有一個 cti 如果 oid 叠代 lambda 字符串 由於

算法不依賴與容器(使用叠代器),但大多數依賴於元素類型。如find需要==運算符,其他算法可能要求支持<運算符。

算法永遠不會執行容器的操作,永遠不會改變底層容器的大小(添加或刪除元素)。

accumulate(v.cbegin(), v.cend(), string(“ ”)) 算法累加運算符,第3個參數的類型決定了使用哪個+號運算符。

equal(v1.cbegin(), v1.cend(), v2.cbegin()),逐個比較兩個序列。第二個序列至少與第一個序列一樣長。

容器和元素類型都不必一樣,只要支持==符號比較兩個元素類型。

那些只接受單一叠代器來表示第二個序列的算法,都假定第二個序列至少與第一個等長。

確保算法不會訪問第二個序列中不存在的元素是程序員的責任。

算法不檢查寫操作面,向目的位置叠代器寫入數據的算法

auto it = back_inserter(vec);//返回插入叠代器,使用它賦值時會調用push_back將元素添加到容器。

fill_n(it,10,0); //添加10個元素到vec。

很多算法提供拷貝版本,返回的是一份拷貝,原序列並未改變。

sort(words.begin(), words.end());//排序

auto end_unique = unique(words.begin(), words.end());//去重,返回不重復區域後面一個位置

words.erase(end_unique,words.end());//算法不能直接增刪,調用erase刪除多余元素

即時words沒有重復元素,此操作也是安全的

謂詞(predicate)

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

用謂詞替代算法默認的操作,如定義二元isShorter函數,替代sort默認的<比較元素。

sort(words.begin(), words.end(), isShorter);

stable_sort(words.begin(), words.end(), isShorter);//維持相等元素的原有排序

partition將容器劃分成兩部分,true的在前。partition_stable 維持原有元素的順序

可調用對象

函數、函數指針、函數對象,lambda表達式(可理解為一個未命名的內聯函數)

[capture list] (parameter list) -> return type {function body}

其中參數列表和返回類型可以省略,但是捕獲列表和函數體必須永遠包含。

忽略參數列表等價於空參數列表

忽略返回類型,如果函數體只有一個return語句則從表達式的類型判斷;如包含其他任意語句,則返回void。

lambda

不能有默認參數

lambda只有在其捕獲列表中的捕獲一個它所在函數的局部變量,才能再函數體中使用該變量。僅限局部非static變量,局部static和所在函數外聲明的變量可以直接使用。

make_plural(count, “word”, “s”) //區分單復數,count為1則單數

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

for_each(wc,words.end(),[](const string &s){cout<<s<<” ”;});

排序-去重-刪除多余-按長度排序-找到長度大於sz的起點,打印

值捕獲

前提是變量可拷貝,被捕獲的變量的值在lambda創建時拷貝,而不是調用時。

引用捕獲

需要保證捕獲的局部變量的有效性,也可以從函數返回lambda,不能包含局部變量的引用捕獲。如果可能,盡量避免捕獲指針或引用。

lambda捕獲列表

[]

空捕獲列表

[names]

名字列表,默認都是被拷貝

[&]

隱式捕獲列表,都采用引用捕獲

[=]

隱式捕獲列表,都采用值捕獲

[&,identifier_list]

除個別值捕獲

[=, identifier_list]

除個別引用捕獲

可變lambda

默認不會改變被捕獲的變量的值,如希望改變,使用mutable。

對於只在一兩個地方使用的簡單操作,lambda表達式是最有用的。

對於在很多地方使用的相同操作,或者一個操作需要很多語句才能完成,通常定義一個函數更好。

捕獲列表為空的lambda,通常可以用函數替代;

而對於有捕獲變量的,就不那麽容易了。會面臨謂詞參數個數不一致的問題。

bind函數(函數適配器)

接受一個可調用對象,生成一個新的可調用對象來適應原對象的參數列表。

auto newCallable = bind(callable, arg_list);

arg_list對應給callable的參數。其中可能包含_n形式的名字,表示占據了傳遞給newCallable參數的第n個位置。

bool check_size(const string& s, string::size_type sz) {return s.size() >= sz;}

auto check6 = bind(check_size, _1, 6);//

bind調用只有一個占位符,便是check6只接收一個參數,占位符出現在第一個arg_list的第一個位置,表示check6此參數對應check_size的第一個參數const string&。

auto g =bind(f, a, b, _2, c, _1);//f有5個參數,其中_1和_2分別對應g的第1個和第2個參數

g(_1,_2)映射為f(a, b, _2, c, _1)

需要包含命名空間using std::placeholders::_1;

using namespace std::placeholders;

對於不是占位符的參數,默認是拷貝到bind返回的可調用對象中的,有時候需要用引用方式傳遞ref(os),cref()生成const引用。

bind ref cref都在頭文件functinal裏。

註:bind1st和bind2nd,分別只能綁定第一個或第二個參數,由於局限性強已經在新標準中棄用。binder1st和binder2nd也類似,只不過他們是模板類需要指定op的類型

bind1st(op, value)(x) -> op(value,x)

bind2nd(op,value)(x) -> op(x,value)

其他叠代器

插入叠代器:綁定到容器上,可以向容器插入元素

流叠代器:綁定到輸入輸出流,可以遍歷關聯的IO流,只要定義了<<或>>運算符的對象

反向叠代器:向後移動,forward_list沒有

移動叠代器:不是拷貝其中的元素,而是移動move它們。

插入叠代器

*it, ++it, it++ 不會對it做任何事情,返回it

back_inserter push_back

front_inserter push_front

inserter insert

如inserter生成的叠代器做如下賦值操作 *it = val;

效果相當於以下代碼

it = c.insert(it, val);//it指向新加入的首元素

++it;

使用不同的插入叠代器,最後生成的序列順序會不同。

流叠代器

創建時必須制定將要讀寫的對象類型,可以在創建時將其綁定到一個流上,也可以默認初始化叠代器當尾後叠代器使用。

istream_iterator<int> in_iter(cin);

istream_iterator<int> eof;

while(in_iter!=eof)

vec.push_back(*in_iter++);

可以改成成如下形式

istream_iterator<int> in_iter(cin), eof;

vector<int> vec(in_iter, eof); //從叠代器範圍構造vec

從流中讀取的數據來構造vec

與某些算法結合,來處理流數據。輸入流叠代器可以作為源叠代器,輸入作為目標叠代器

如cout<<accumulate(in, eof, 0)<<endl;

istream_iterator允許懶惰求值,推遲中流中讀取數據。標準庫保證在第一次解引用前,從流中讀取數據的操作已經完成。

如果叠代器可能銷毀,或者兩個對象同步使用一個流,那麽何時讀取就很重要了。

ostream_iterator允許第二個參數,是C風格的字符串,在輸出每個元素之後都會打印此字符串。必須綁定到一個流。

*out, ++out, out++提供形式支持,不會做任何事情

ostream_iterator<int> out_iter (cout, “ “);

for (auto e : vec)

*out_iter++ = e;

cout<<endl;

其中*和++都可以忽略,但是推薦這種寫法,可以保持與其他叠代器的使用保持一致,修改容易,而且對讀者來說也更為清晰。

反向叠代器

使我們可以使用算法透明地向前或向後處理容器。

需要既支持++也支持--,forward_list和流叠代器不能創建。

當使用反向叠代器,在打印的時候會反向,需要使用base成員來轉換。

[line.crbegin(), rcomma)和[rcomma.base(), line.end())指向line中相同的元素範圍。

另外反向叠代器表示的範圍是不對稱的,當從普通叠代器初始化反向叠代器或是給其賦值的時候,其指向並不是相同的。

5種叠代器類型

叠代器類別

輸入叠代器

只讀,不寫;單遍掃描,只能遞增

輸出叠代器

只寫,不讀;單遍掃描,只能遞增

前向叠代器

可讀寫;多遍掃描,只能遞增

雙向叠代器

可讀寫;多遍掃描,可遞增遞減

隨機訪問叠代器

可讀寫;多遍掃描,支持全部叠代器運算

算法指明了叠代器參數的最小類別,如傳遞的叠代器參數基本更低會產生錯誤。

輸入叠代器:

至少支持==, !=, 前後置++, 解引用*(賦值的右側), ->

*it++保證是有效的,但是遞增可能導致其他叠代器失效,不能保證輸入叠代器的狀態可以保存下來並用來訪問元素。因此只能用於單遍掃描算法。

如find和accumulate支持istream_iterator

輸出叠代器:(可看做輸入叠代器的補集)

至少支持前後置++, 解引用*(賦值的左側),只能向一個輸出叠代器賦值一次,單遍掃描。

如copy第3個參數,ostream_iterator

前向叠代器:

replace, forward_list

雙向叠代器:(--)

reverse

隨機訪問叠代器:

< <= > >= + += - -= 兩個叠代器相減 下標運算符

sort array deque string vector 內置數組元素的指針

常見算法形參類型

alg(beg, end, other args);

alg(beg, end, dest, other args);

alg(beg, end, beg2, other args);

alg(beg, end, beg2, end2, other args);

向輸出叠代器接入數據的算法都是假定目標空間足夠。

單個beg2的算法假定從beg2開始的序列至少與序列1一樣大。

算法命名規範

重載傳遞一個謂詞

_if版本 將接受一個元素值版本改成接受一個謂詞版本,使用不同的函數名避免可能的重載歧義。

copy版本

默認重排元素的算法寫回輸入序列,copy版本寫到一個指定的輸出目的位置。

鏈表類型特有算法

其他通用算法也可以用於鏈表,但是代價過高,需要交換元素。鏈表可以通過改變鏈接來完成交換,而不用真正交換元素。所以優先使用成員函數版本。

list forward_list成員函數版本算法(都返回void)

lst.merge(lst2)

合並,兩個都必須是有序的,合並之後lst2為空

lst.merge(lst2,comp)

同上,使用謂詞給定的比較操作

lst.remove(val)

erase刪除元素

lst.remove_if(pred)

同上

lst.reverse()

反轉序列

lst.sort()

使用<或給定比較操作排序

lst.sort(comp)

lst.unique()

刪除同一個值得連續拷貝

lst.unique(pred)

同上,使用二元謂詞

splice成員

lst.splice(args)或flst.splice_after(args)

(p, lst2) 將lst2中所有元素移動到lst中p之前或flst中p之後,元素將從lst2刪除,不能為同一個鏈表

(p, lst2, p2) 將p2指向的元素移動到lst中,可以是相同的鏈表

(p, lst2, b, e) 將b和e之間指向的元素移動到lst中,可以是相同的鏈表,可以是同鏈表但不能包含p。

鏈表特有版本一個重要的區別是會改變底層的容器。

C/C++基礎----泛型算法