C++自修入門實境秀、C++ Primer 5版研讀秀 69/ ~ v11關聯式容器 Chapter 11~11.1. Using an As...



頁420

Associative containers support efficient lookup and retrieval by a key.

The two primary associative-container types are map and set .

The elements in a map are key–value pairs:

一個map中的元素是鍵值與值對組(key-value pairs):

對組,還真是文言文呢!→加個「的」就白話點了:鍵值與值的對組 第69集1:20

The key serves as當作 an index into the map , and the value represents the data associated with that index.

第69集 3:00這裡用「into」就像迭代器也用這個介係詞,就知道index(key)是一樣或類似的東西了 不見題目只見關鍵字value就是類似解參考迭代器之後的元素值

A set element contains only a key; a set supports efficient queries as to whether a given key is present.

A dictionary would be a good use for a map : The word would be the key, and its definition would be the value.

字典(dictionary)會是map的好用處:字詞會是鍵值,而其定義則是值。

→ 第69集 7:00 如果要編字典,map就應該would委婉、推測、假設派得上用場

字典就是map應用的一個範例

∵ 模範生就是好榜樣 ∴ 得上、好→範

此句應翻成:如果說would假設、比況,字典是map應用的一個絕佳示範,那麼字典中的字詞,就如map的鍵值(key),而該字詞的定義,就是那個值(value)。



17:40

程式庫提供了8個關聯式容器,見表11.1

這8個又可以從以下3個面向來作區別:

(1)它們是set還是map?

(2)鍵值是否重複?是否有重複(multiple,can appear multiple times 59:20)的鍵值,或不可重複(i.e.唯一、絕對的鍵值)

(3)鍵值可有排序?元素的儲存(從上下文來看,鍵值與元素應是同步的)是有序的還是無序的

These eight differ along three dimensions:

這八種之間的差異可用三個維度描述

多重鍵值(multiple keys)

25:20

若允許多重鍵值(重複鍵值?)的容器,會包括關鍵字「multi」

鍵值是無序的則會在其容器前冠上「unordered」(未經排序的)字樣(i.e.關鍵字)

31:30

就好像前面演算法的命名規則naming convention(10.5.3. Algorithm Naming Conventions)

關聯式容器也有其命名規則:

Hence an unordered_multi_set is a set that allows multiple keys whose elements are not stored in order, whereas(=however) a set has unique keys that are stored in order.

一個unordered_multi_set就是允許多重鍵值,而且元素並沒有依序儲存的集合(set),而一個set則是只具有唯一鍵值,而且這些鍵值是依序儲存的。

中文版「unordered_multi_set」誤作「unordered_multiset」

這是拿unordered_multi_set 和set (i.e.沒有前綴字的plain或ordinary的set)來對比。謂若一個set是只有唯一鍵值且是有序的。

無序的容器是用hash函式來組織它們的元素,在11.4(頁443)再詳述。

hash function(雜湊函數)

map和multimap是定義在map標頭檔

set和multiset是定義在set標頭檔

無序容器則分別在unordered_set和unordered_map標頭檔中



53:30

Table 11.1. Associative Container Types

表11.1:關聯式容器型別

元素以鍵值排序

map 關聯式陣列;存放鍵值與值對組(key-value pairs)

set 鍵值就是值的容器the key is the value

multimap 一個鍵值能在其中出現多次的map

multiset 一個鏈值能在其中出現多次的set

無序群集

unordered_map 以一個hash(雜湊)函數組織的map

unordered_set 以一個hash函數組織的set

unordered_multimap 經雜湊(hashed)的map ;鍵值能夠出現多次

unordered_multiset 經雜湊的set ;鏈值能夠出現多次

11.1. Using an Associative Container

1:2:59

群集(collection)

each pair might contain a person’s name as a key and a phone number as its value. We speak of such a data structure as “mapping names to phone numbers.”

人名好像地圖,而電話號碼好像地物、地點,由人名找到對應到電話號碼就如依地圖找到地點、地圖一樣

map通常被叫作關聯式陣列(associative array)

map 型別常被稱作關聯式陣列(associative array)。

頁421

關聯式陣列(associative array)就如一般的陣列,只是它的索引值不必是整數!對其做下標(subscript)運算時不必傳遞整數。

一個map 中的值是以一個鍵值找到,而非藉由它們的位置。



Given a map of names to phone numbers, we’d use a person’s name as a subscript to fetch that person’s phone number.

給定名字對電話號碼的一個map

names to 對應、對映、映射到phone numbers

Given a map of names to phone numbers如果有一個由人名可以找到電話號碼的map



bad checks 空頭支票 開空頭支票,「開」用 write

1:24:40

Using a map

仰仗關聯式陣列(associative array)的典型範例就是一個像word-counting這樣的程式:

仰賴關聯式陣列的一個經典範例是字詞計數程式:

字頻詞頻程式

藉由字找到該字的頻率(count)所以字是地圖(map)而頻率是地點、地物

from地圖to地點

// count the number of times each word occurs in the input

map<string, size_t> word_count; // empty map from string to size_t

string word;

while (cin >> word)

++word_count[word]; // fetch and increment the counter for word

for (const auto &w : word_count) // for each element in the map

// print the results

cout << w.first << " occurs " << w.second

<< ((w.second > 1) ? " times" : " time") << endl;

first就是key,second就是value 這樣的一對(pair)對組

1:38:20

Like the sequential containers, the associative containers are templates (§ 3.3 , p. 96 ). To define a map , we must specify both the key and value types.

是模板(templates)的,在使用時定義一個關聯式容器(associative container)時都要加上角括弧<>來指定用來組建的型別

map<string, size_t>

這裡string就是key的型別,而size_t就是value的型別

當我們對map物件下標(subscript)時,我們傳遞的是這裡的stirng:

When we subscript word_count , we use a string as the subscript, and we get back the size_t counter associated with that string .

所以才叫關聯式陣列(associative array)或關聯式容器(associative container)

If word is not already in the map , the subscript operator creates a new element whose key is word and whose value is 0 .

第一個傳入的word,這個word鍵值key對應的關聯的值value就會是0。是由下標運算子([]運算子,subscript operator)來建立的元素

我們可以取巧將對組(pair)一對呼之戲稱為老伴、牽手(value就是key的另一伴。)

因此,這句就可以翻成,下標運算子會締結促成一對佳偶,男的就是key,目前有word的值;而他的另一伴就是value,目前的值則是0。

可以說map的元素值是一種複合的值,聚合了鍵值索引值——key,MS Access資料庫中就叫索引值與值、記錄的複合、結合的值。

2:3:00 原來pair還是一種型別:

When we fetch an element from a map , we get an object of type pair , which we’ll describe in § 11.2.3 (p. 426 ). Briefly, a pair is a template type (模板型別)that holds two ( public ) data elements named first and second .

2:26:00

#include<iostream>

#include<iterator>

#include<map>

using namespace std;

int main() {

istream_iterator<string>in(cin), end;

map<string, string::size_type>m;

while (in != end)

{

++m[*in];

++in;

}

//ostream_iterator<int>out(cout, ",");//無法用,因map元素為pair型別

for(auto var : m)

cout<<var.first << " " <<var.second<< endl;

}

頁422

Using a set

2:31:09

set可翻成「些」或「組」

2:48:00

// count the number of times each word occurs in the input

map<string, size_t> word_count; // empty map from string to size_t

set<string> exclude = {"The", "But", "And", "Or", "An", "A",

"the", "but", "and", "or", "an",

"a"};

string word;

while (cin >> word)

// count only words that are not in exclude

if (exclude.find(word) == exclude.end())

++word_count[word]; // fetch and increment the counter for word



#include<iostream>

#include<iterator>

#include<map>

#include<set>

using namespace std;

int main() {

set<string>st{ "the","an","and","or" ,"be","at","if","to","of"};

istream_iterator<string>in(cin), end;

map<string, string::size_type>m;

while (in != end)

{

if (st.find(*in) == st.cend())

{

++m[*in];

}

++in;

}

for (auto var : m)

cout << var.first << " " << var.second << endl;

}

2:49:33

set也是模板(template)

關聯式容器(associative container)也可以串列初始化(list initialization):

As with the sequential containers, we can list initialize (§ 9.2.4 , p. 336 ) the elements of an associative container.

set的find()的成員函式

練習11.1

3:6:30 map和vector加入元素的方式好像一樣。然而map的每個元素有一「對組(pair)」值,而vector沒有。插入新元素須分別指定這對組值各個成員的值;尋找亦須由鍵值(key,即對組值的first成員)來找,不能用位置來下標(subscript)。

練習11.2

3:8:40

如果要隨機存取,須具備隨機存取迭代器(random-access iterator)的容器(container)

如果要從尾端插入新元素,則vector、deque、list都能用。若要隨機插入,用插入迭代器(insert iterator)也都能用,效率可能也差不多。首端插入則deque、list應該才好。

list在排序等演算法中,應用其成員函式的演算法會更有效率。

set主要是用在儲存一個需要檢查、調閱用的串列

map主要是用在已知一個元素鍵值想來查找它對應的值的地方。而map既然有關聯式陣列(associative array)的美名,則應該會與一般陣列(內建型別(builtin type)陣列)一樣有效率。



Give an example of when each of list, vector, deque,map, and set might be most useful.

an example

對獎

排班、行事曆

訂位系統



練習11.3

文件詞頻

#include<fstream>

#include<iterator>

#include<map>

#include<set>

using namespace std;

int main() {

set<string>st{ "the","an","and","or" ,"be","at","if","to","of"};

ifstream fs("V:\\Programming\\C++\\Untitled-1.txt");

istream_iterator<string>in(fs), end;

map<string, string::size_type>m;

while (in != end)

{

if (st.find(*in) == st.cend())

{

++m[*in];

}

++in;

}

ofstream ofs("V:\\Programming\\C++\\out.txt");

for (auto var : m)

ofs << var.first << " " << var.second << endl;

}

4:10:50

練習11.4

複習前面

3.2.2. Operations on string s

3.2.3. Dealing with the Characters in a string

ispunct函式

5:25:20

5:40:10

Table 3.3. cctype Functions

6:24:00

#include<fstream>

#include<iterator>

#include<map>

#include<set>

using namespace std;

int main() {

set<string>st{ "the","an","and","or" ,"be","at","if","to","of" };

ifstream fs("V:\\Programming\\C++\\Untitled-1.txt");

istream_iterator<string>in(fs), end;

map<string, string::size_type>m;

while (in != end)

{

string s(*in);

for (decltype(s.size()) i = 0; i != s.size(); ++i)

{

if (ispunct(s[i]))

{

s.erase(i, 1);

--i;

}

else

s[i] = tolower(s[i]);//st都是小寫,所以改成小寫,以供st.find(s)比對

}

if (st.find(s) == st.cend())

{



++m[s];

}

++in;

}

ofstream ofs("V:\\Programming\\C++\\out.txt");

for (auto var : m)

ofs << var.first << " " << var.second << endl;

}

6:31:10 6:42:00 6:49:00

改用泛用演算法來寫 6:50:40測試

失敗,再研究 7:24:00

Adding Literals and strings

第69集 4:29:00

The string library lets us convert both character literals and character string literals (§ 2.1.3, p. 39) to strings. Because we can use these literals where a string is expected, we can rewrite the previous program as follows:

string s1 = "hello", s2 = "world"; // no punctuation in s1 or s2

string s3 = s1 + ", " + s2 + '\n';

單引號的就是character literals 雙引號括起來的則是character string literals

頁90

When we mix strings and string or character literals, at least one operand to each + operator must be of string type:

這裡的「string」指的是character string literals,不是string

字串字面值(string literal)和程式庫型別(library type)string 是不同的型別!

string s4 = s1 + ", "; // ok: adding a string and a literal

string s5 = "hello" + ", "; // error: no string operand

string s6 = s1 + ", " + "world"; // ok: each + has a string operand

string s7 = "hello" + ", " + s2; // error: can't add string literals

「s1 + ", " + "world"」是先評估左邊的「s1 + ", "」結果是string型別,再評估「string+ "world"」所以才可以。 第69集 4:37:10

同理「"hello" + ", " + s2;」則是先評估了左邊的「+」,這個「+」運算子兩邊都是字串字面值(string literal),並沒有string型別(程式庫型別)所以是錯的



3.2.3. Dealing with the Characters in a string

第69集 4:50:00

頁91

It turns out that the best way to deal with these cases involves different language and library facilities.

我們可以看到,應付這些情況的最佳方式涉及了不同的語言和程式庫機能。

這語言是人的語言,還會程式語言?⑧找對主詞 ⑨對錯問題,回到二作 當然是程式語言

一組程式庫函式……這些函式定義於cctype標頭



因為只是承繼自C語言的,所以刪除副檔名的.h,而再在檔名前冠上c來註明此標頭實是C語言的版本,不是C++的:

(C語言程式庫)這些標頭的C++版本則被命名為cname,移除了後綴的.h並在前面加了字母c。c代表的是此標頭為C程式庫的一部分。

因此, cctype與ctype.h有相同的內容,但存在的形式適用於C++。

特別是,定義在cname 標頭中的名稱是被定義在std命名空間內,而定義在.h版本中的那些則否。

第66集 5:0:18 也就是C++的版本會獨立在標頭檔.h中,而C語言的版本則是被定義在std命名空間中。

一般來說,C++程式應該使用cname版本的標頭,而非name.h版本的。

也就是要用C程式庫的,而不是C++程式庫的版本!?

如此一來,源自標準程式庫的名稱就能一致地於std命名空間中被找到。若使用.h標頭,就等於把責任放在程式設計師身上,要他們記得哪些程式庫名稱繼承字C,而哪些是C++獨有的。

字→自

因為C語言的被併到std中了,反而用C語言的版本,才能一致地於std命名空間中被找到



Processing Every Character? Use Range-Based for

Range for



for (declaration:expression)

statement

其中expression是型別代表一種序列的一個物件,

→其中expression是代表序列型別的一個物件。

where expression is an object of a type that represents a sequence,

第69集 5:17:00

一個string代表一個序列的字元,

程式庫string型別與字元的關係。序列就是後面提到的循序容器(sequential container)。



string s("expression");

for (auto c:s)

{

cout << c << endl;

}

這裡的auto判斷其實就是char



迴圈控制變數

即迴圈內定義的區域變數

第69集 5:21:40

頁92

ispunct函式

參見練習11.4

舉個稍微複雜一點的例子,我們會用一個範圍for和ispunct函式來計數一個string中的標點符號字元(punctuation characters)的數目:

string s("Hello World!!!") ;

// punct_cnt的型別與s.size回傳的相同,參閱§2.5.3(頁70)

decltype(s.size()) punct_cnt = 0; //計數s中的標點符號字元之數量

for (auto c : s) //對s中的每個字元

if (ispunct(c)) //如果該字元是一個標點符號

++punct_cnt; //遞增標點符號計數器

cout << punct_cnt

<< " punctuation characters in " << s << endl;

Table 3.3. cctype Functions

表 3.3 : cctype 函式

isalnum(c) 如果c是一個字母(letter)或數字(digit)就為true。

isalpha(c) 如果c是一個字母就為true。

iscntrl(c) 如果 c 是一個控制字元(control character)就為 true。

isdigit(c) 如果c是一個數字就為true。

isgraph(c) 如果c不是一個空格(space)但是可列印的(printable)就為true。

islower(c) 如果c是一個小寫(lowercase)字母就為true。

isprint(c) 如果c是一個可列印的字元(即一個空格或有可視表達形式的字元)就為 true 。

ispunct(c) 如果c是一個標點符號字元(punctuation character,即不是控制字元、數字、 字母或可印空白的字元)就為true。

isspace(c) 如果 c 是空白(whitespace,即一個 space、tab、vertical tab、return、 newline 或 form feed )就為 true 。

isupper(c) 如果c是一個大寫(uppercase)字母就為true。

isxdigit(c) 如果c是一個十六進位數字(hexadecimal digit)就為true。

tolower(c) 如果c是一個大寫字母,就回傳其小寫;否則原封不動地回傳c。

toupper(c) 如果c是一個小寫字母,就回傳其大寫;否則原封不動地回傳c。

用c表char(character) 第69集5:36:10

al:alphabet

graph:指圖表用的定位符號



頁93

Using a Range for to Change the Characters in a string

留言

熱門文章