C++自修入門實境秀、C++ Primer 5版研讀秀 95/ ~12.3.1. Design of the Query Program~練習1...
頁438 第95集開始
Iterator-Oriented可譯為「迭代器模式」(行為模式的模式)左包含區間 迭代器範圍(iterator range)
lower_bound()
upper_bound()
即兩個夾起來的邊界範圍Iterator Ranges
如果在一個multimap中找不到指定的鍵值,那麼:lower_bound()==upper_bound()
這兩個成員函式傳回的迭代器會指向可以插入該鍵值元素的適當位置:
both will refer to the point at which the key can be inserted without disrupting the order.
頁439
而當鍵值並不存在於容器時,且它又大於所有容器中的鍵值的話,那麼用這鍵值來下達lower_bound指令,也會回傳一個尾端後迭代器(off-the-end iterator)。
注意:由lower_bound回傳的迭代器也可能不會指向容器中的任何元素。如果鍵值在原容器中並沒有被找到,那麼lower_bound回傳的迭代器就會指向可以插入新元素的地方:這個位置就是不會影響到元素排序,且可以插入新元素的第一個位置。
運用這樣的特性,我們就可以改寫之前的那個程式碼了:
//authors和search_item的定義一如前面(as above),茲略
//以beg和end來畫出一個某個作者作品的元素範圍
for(auto beg=authors.lower_bound(search_item),end=authors.upper_bound(search_item);beg!=end;++beg)
cout<<beg->second<<endl;//將每一部作品的名稱給印出來
這個程式碼是和前面那個等效的。前面的那個用了count和find這兩個成員函式來達成我們的目的,在操作上也直觀許多。而在這個程式碼中,只要在authors這個容器內存在search_item這樣鍵值的元素,那麼lower_bound成員函式就會將beg設成指向符合這search_item鍵值的第一個元素。若容器中並沒有這樣的元素,那麼beg就會指向那個比search_item還大一點的元素,當然beg也可能會是一個尾端後的迭代器(off-the-end iterator)。對upper_bound的呼叫則會將end設定成為一個迭代器,指向最後符合查找鍵值的元素後面的那個位置或元素。這樣的運算毋庸去擔心那個要查找的鍵值是否存在,它的目的在於,回傳的要是一個類似於迭代器的範圍(iterator range,§9.2.1,頁331)。
如果鍵值並不存在,那麼lower_bound和upper_bound所回傳的值就會相等,都會是一個指向適合插入新元素位置的迭代器。
只要有符合查找鍵值的元素,beg就會是一個指向第一個符合此鍵值元素的迭代器。接著就遞增beg來逐一巡覽所有符合此鍵值的元素。而end存放的迭代器則會告知我們是否這樣的巡覽已經完畢。一旦beg和end相等,也就表示這樣的巡覽已完成。
第95集56:29
因為beg、end這兩個迭代器組成了一個迭代器範圍,所以我們就可以用for迴圈來逐一巡覽這範圍內的所有元素。這樣就可以一一印出我們要找的作者他的所有的作品出來。如果這個作者並不存在,那麼beg和end在一開始就會相等,當然這個for迴圈就不會執行。如果作者作品存在,那麼對beg做遞增的話,其值終究會和end相等;而在每一次的迴圈中,都能將該作者的一部作品名稱給印出來。
注意:只要用同一鍵值來呼叫lower_bound和upper_bound,而其回傳的值是相等,那麼可以確定的是:要找的鍵值就決定不存在!
The equal_range Function
關聯式容器的equal_range成員函式
1:2:17第95集 1:16:50
第3種達成我們前述需求的方式卻是最直觀的方式,也就是改用equal_range成員函式,而不要用到upper_bound和lower_bound這兩個。
equal_range()
此一成員函式也不過就是把lower_bound與upper_bound合併嘛
equal ⑦先抓動詞 ⑧找對主詞 與誰equal?與鍵值equal
頁440
這個成員函式接受一個鍵值作為引數,而回傳的卻是一個由兩個迭代器所組成的pair物件。如果鍵值存在,那麼pair的first就會是一個指向第一個符合鍵值元素的迭代器,而second就會是一個指向最後一個符合鍵值元素後的位置的迭代器。【也就是由這對迭代器組成一個迭代器範圍】如果找不到要找的鍵值,那麼這對迭代器就會是等值的、同時指向適合插入新元素的位置。
因此,用equal_range改寫之前的程式,結果會像這樣:
//authors和search_item的定義一如之前
//pos存放的是一對迭代器,它界定了符合要找鍵值的元素範圍
for(auto pos=authors.equal_range(search_item);pos.first!=pos.second;++pos.first)
cout<<pos.first->second<<endl;//印出想找的作者其每一部作品的名稱
這段程式碼與前一個用到upper_bound和lower_bound的大致相同,只是這裡不再使用區域變數beg和end來存放迭代器範圍,我們改用equal_range所回傳回來的pair來指出這個範圍。在這個pair中的first成員所存放的迭代器就與lower_bound回傳的那個完全一樣;而second存放的就和upper_bound沒什麼兩樣。因此在此段程式碼中,pos.first就相當於beg,而pos.second就如同end。
第95集 1:29:59
練習11.27
碰到怎樣的問題您會想要count解決?而何時才會用find呢?
count():需要計數時才用上。若只是想要知道某鍵值的元素存不存在關聯式容器中就不必了,用find()即可。且count也不能對元素進行操作,而find回傳的非唯讀迭代器則可以對元素進行操作(在關聯式容器中也僅限於非唯讀的map類型的容器才能對其元素的「值」部分進行更動)
這題是考妳選擇適當的方法來解決問題是很重要的。
中文版翻譯不當:
何種問題你會使用count來解決?什麼你會改用find ?
練習11.28
1:35:16第95集
定義且初始化一個變數來存放在map上進行find運算回傳的值,這個map是由string映射(map)到由int組成的vector。
中文版翻得不好,以後就不再一一為之訂正了:
定義並初始化一個變數來存放在一個string對int所成vector的map上呼叫find的結果。
#include<iostream>
#include<map>
#include<vector>
using namespace std;
int main() {
vector<int>v{1,2};
map<string, vector<int>>m;//{ "營利事業所得稅",v },
m["孫守真"] = v;
string s("守真");
map<string, vector<int>>::iterator it{m.begin()};//定義且初始化
it= m.find(s);//儲存find回傳的值
}
練習11.29
1:59:40
當我們將一個不在容器中的鍵值分別傳給upper_bound、lower_bound和equal_range時,它們會回傳怎樣的值?
lower_bound和upper_bound傳回的值會相等,都是指向容器中可以插入此鍵值元素的位置。
而equal_range回傳的值也和前2者一樣。——即當尋找的鍵值並不存在於容器中,則3者回傳的值是一樣的!
其實在用這3個成員函式時也未必會接著插入新元素,因此此回傳的值也可以是表示想找的鍵值,最接近它的是容器中的哪個位置(或哪兩個元素之間,或最後面)
#include<iostream>
#include<iterator>
#include<map>
#include<vector>
using namespace std;
int main() {
vector<int>v{ 1,2 };
map<string, vector<int>>m;
m["孫守真"] = v;
istream_iterator<string>in(cin), end;
while (in != end)
m.insert(make_pair(*in++, v));
string s("守真");
map<string, vector<int>>::iterator it = m.find(s);
map<string, vector<int>>::iterator itL = m.lower_bound(s);
map<string, vector<int>>::iterator itU = m.upper_bound(s);
pair<map<string, vector<int>>::iterator, map<string, vector<int>>::iterator>
itE = m.equal_range(s);
if (itL == itE.first)
cout << "lower=first" << endl;
if (itU == itE.second)
cout << "upper=second" << endl;
if (itL == itU)
cout << "not found" << endl;
if (itE.first == itE.second)
{
cout << "not found" << endl;
cout << (itE.first)->first << endl;//可見可插入位置也如前循序容器的insert都是在前位插入要插入的元素
m.insert(itE.first, make_pair(s, v));
}
decltype(m.cbegin()) itM = m.cbegin();
while (itM != m.cend())
{
cout << itM++->first << endl;
}
}
練習11.30
2:20:40
解釋在本區段課文中的最後那個程式,它程式碼的輸出部分這個「pos.first->second」運算元的意思。
//authors和search_item的定義一如之前
//pos存放的是一對迭代器,它界定了符合要找鍵值的元素範圍
for(auto pos=authors.equal_range(search_item);pos.first!=pos.second;++pos.first)
cout<<pos.first->second<<endl;//印出想找的作者其每一部作品的名稱
pos是一對迭代器,取其第一個,指向的是找到的元素。解參考這個迭代器,就得到該元素(但若沒到或是回傳尾端後迭代器就會出錯;但有「pos.first!=pos.second」把關,就不會出錯了)。而authors是個map<stiring,string>,所以其元素的second部分,就是第2個string(「,string>」也就是作者的作品名稱),把它印出來。
因為equal_range()成員函式傳的是pair<map::iterator, map::iterator >,所以pos.first就是逗點前一個iterator,將此迭代器解參考後即是map的元素,而存取map元素的鍵值與值的對組(pair)中的第2個資料成員(data member)pos.first->second,在這裡就是作者的作品。
2:53:20
練習11.31
2:29:50 3:27:40
3:33:05
寫一個程式,內含一個由作者關聯到其作品的multimap。使用find成員函式來找出在這個multimap中的某個元素,並調用erase成員函式來刪除它。要留意您的程式在找不到要找的對象時還能正確地執行
Write a program that defines a multimap of authors and their works. Use find to find an element in the multimap and erase that element. Be sure your program works correctly if the element you look for is not in the map.
此map,應當作普通字體,表示multimap。
中文版也未訂正。
using mss = multimap<string, string>;
mss authors;
//加入第一個元素到authors中,它的鍵值是「Barth,John」
authors.insert({ "Barth,John","Sot-Weed Factor" });
//這也是可以的:加入第二個也是「Barth,John」這樣鍵值的元素到authors中
authors.insert({ "Barth,John","Lost in the Funhouse" });
authors.insert({ "S,John","Lost in the Funhouse" });
cout << authors.size() << endl;
cout << authors.count("Barth,John") << endl;
while (authors.count("Barth,John")>0)
{
authors.erase(authors.find("Barth,John"));
}
cout << authors.size() << endl;
#include<iostream>
#include<iterator>
#include<map>
using namespace std;
int main() {
multimap<string, string>m;
istream_iterator<string>in(cin), end;
string a;
while (in != end)
{
a = *in;
m.insert(make_pair(a, *++in)); ++in;
}
string s("孫守真");
multimap<string, string>::iterator it = m.find(s);//此下三式用map也行,不必用multimap
multimap<string, string>::iterator itL = m.lower_bound(s);
multimap<string, string>::iterator itU = m.upper_bound(s);
pair<multimap<string, string>::iterator, multimap<string, string>::iterator>itE = m.equal_range(s);
if (itL == itE.first)
cout << "lower=first" << endl;
if (itU == itE.second)
cout << "upper=second" << endl;
if (itL == itU)
cout << "not found" << endl;
if (itE.first == itE.second)
{
cout << "not found" << endl;
cout << (itE.first)->first << endl;//可見可插入位置也如前循序容器的insert都是在前位插入要插入的元素
}
if (itL != itU&&it!=m.end()&& itE.first!=itE.second)
m.erase(s);
decltype(m.cbegin()) itM = m.cbegin();
while (itM != m.cend())
{
cout << itM++->first<<":"<< itM->second << endl;
}
}
練習11.32
3:54:10
用前一練習的multimap來寫一個程式以照字母順序印出作者與其所有的作品。
已詳見前一題。今再訂如下:
5:14:00
using mss = multimap<string, string>;
using pmssi = pair<mss::iterator, mss::iterator>;
mss authors;
//照作品字母順序插入元素
istream_iterator<string>in(cin), end;
string s;
while (in != end)
{
s = *in++;
pair<string, string> au_work = make_pair(s, *in++);
//沒錄到的見臉書直播488集1:48:00前後 https://www.facebook.com/oscarsun72/videos/2553453558099096
if (authors.size() == 0)
authors.insert(au_work);
else
{
if (authors.count(au_work.first) == 0)
authors.insert(au_work);
else//作品排序
{
mss::iterator mp = authors.end();
for (pmssi pos = authors.equal_range(au_work.first); pos.first != pos.second; pos.first++)
{
if (pos.first->second >= au_work.second)//insert都是前位插入,所以既有的作品要大於要插入的,才會插在其前
{
mp = authors.insert(pos.first, au_work);//記下有沒有插入成功
break;
}
}
if (mp == authors.end())
authors.insert(au_work);//若作品字母順序是現有中最大的,直接插入,就會墊底
}
}
11.3.6. A Word Transformation Map
用於文字轉換的map
11.3.6 一個字詞變換映射
中文版翻成這樣,誰看得懂?!
5:17:10 6:8:50
作為11.3在關聯式容器上的運算這一節的總結,我們會寫出一個程式來演示如何在一個map容器中插入、尋找以及巡覽元素。您們將看到這個程式如何將一個文字轉換為另一個。有兩個文字檔案會當作這個程式輸入用的檔案。第一個的內容會是轉換的依據,它的每一行,就是每一條轉換的依據,這條依據是由一個要轉換的文字,以及取代它的片語所組成的。5:27:20 6:16:20設計這個程式的想法是:不管這個文字是出現在要被轉換文檔的哪個部分,我們都會將它置換為與其對應的片語。而另一個檔案就是用來轉換用的樣本檔。
如果文字轉換憑據的規則檔案內容是像這樣:
頁441
brb be right back
k okay?
y why
r are
u you
pic picture
thk thanks!
l8r later
且用來轉換的樣本檔案又如這般:
where r u
y dont u send me a pic
k thk l8r
那麼這個程式就應該能夠產生像下面這樣的結果:
where are you
why dont you send me a picture
okay? thanks! later
The Word Transformation Program
6:21:20這個文字轉換程式
我們的解決方案將會是一個用到3個函式的程式。這3個函式的第1個是word_transform函式,它是整個處理流程的中心,負責統領(彙整)全局。它會有兩個ifstrem的引數:第一個會與作為文字轉換憑據的那個檔案繫結(will be bound),第二個ifstrem引數則會和要被轉換的樣本檔案繫結。而第2個要用到的函式則是buildMap,它會讀取文字轉換憑據檔的內容,並創建出一個map來儲存該內容所界定的所有規則——即什麼樣的文字,會轉換為怎麼樣的片語。第3個要用到的函式則是transform,它會有一個string的引數;而只要找到了需要轉換的文字,那麼它就會回傳轉換的結果,否則就回傳這個string引數來表示不必/未經轉換。
我們會先從定義word_transform這個函式開始;它最核心的部分(important parts)就是呼叫buildMap和transform這兩個函式的那兩段程式碼:
參見Chapter 12. Dynamic Memory「文字查詢程式」
void word_transform(ifstream &map_file, ifstream &input)
{
auto trans_map = buildMap(map_file); //讀取轉換憑據的檔案,並記下轉換規則
string text; //用text來存放待轉換樣本檔每行的內容
while(getline(input,text)) //每次讀取一行待轉換的樣本檔內容
{
istringstream stream(text);//讀取每行中的每個文字
string word;
bool firstword = true; //作為記下是否已經印出一個半形空格的標記
while (stream >> word)
{
if (firstword)
firstword = false;
else
cout << " "; //如果還沒印出半形空格就印出來,以便將兩字隔開
//transform函式會回傳它第一個string的引數word,或它轉換的結果
cout << transform(word, trans_map); //印出轉換的結果
}
cout << endl; //結束待轉檔此行文字的轉換工作
}
}
第95集6:39:59 6:52:00
void word_transform(ifstream &map_file, ifstream &input)
{
auto trans_map = buildMap(map_file); // store the transformations
string text; // hold each line from the input
while (getline(input, text))
{ // read a line of input
istringstream stream(text); // read each word
string word;
bool firstword = true; // controls whether a space is printed
while (stream >> word)
{
if (firstword)
firstword = false;
else
cout << " "; // print a space between words
// transform returns its first argument or its transformation
cout << transform(word, trans_map); // print the output
}
cout << endl; // done with this line of input
}
}
頁442
7:11:50 7:26:40
這個word_transform函式是從呼叫buildMap來創建一個文字轉換用的map開始的。我們將這個map存放在trans_map這個變數中。這個函式的其餘部分則是在處理用來轉換的樣本檔內容,而這樣的內容是儲存在input這個ifstream引數中的。while迴圈用了string類別的getline函式來一次讀取一行樣本檔的內容,這樣我們讀出來的每一行就會帶著一個分行標記(line breaks),一如樣本檔中的那樣。【即讀到的每一行,就是原檔每一行元元本本的內容】為了在每行中取得每個文字(或字彙words),我們在while中又用了一個while,這個巢狀內的迴圈則利用了istringstream(§8.3,頁321)來處理目前所在行的每一個字。
巢狀內的while會經由一個bool的firstword變數來判斷是否要印出一個半形空格。對於transform函式的調用則是為了要取得轉換的結果,好將之印出。transform函式所回傳的,要嘛是它引數word這個string,要嘛就是這引數在trans_map這map容器中所對應到的轉換結果。
Building the Transformation Map
建置文字轉換用的map容器
中文版這裡也是亂翻:
建置變換映射
誰看得懂?!
這裡map指的是map這個型別的容器,也不該翻出來
→用一個map容器來配置轉換文字用的資料
buildMap函式會讀取那個文字轉換憑據的檔案來建置一個文字轉換用的map。
map<string, string>buildMap(ifstream &map_file)
{
map<string, string> trans_map; //用來儲存文字轉換規則的map容器
string key; //表示文字轉換規則中待轉之字
string value; //用來取代轉換字的片語
//將ifstream中的第1個字儲存到key中作為map中的鍵值,而其同一行中其餘的部分,則存放在value,作為map元素的「值」,是要取代原文字的片語
while(map_file>>key && getline(map_file,value))
if (value.size() > 1) //檢查若有轉換規則存在;因為包含了前綴的半形空格,所以size最少是1,而不是0。
trans_map[key] = value.substr(1); //略去原字串stringvalue前綴的半形空格
else
throw runtime_error("no rule for " + key);
return trans_map;
}
8:12:00
map<string, string> buildMap(ifstream &map_file)
{
map<string, string> trans_map; // holds the transformations
string key; // a word to transform
string value; // phrase to use instead
// read the first word into key and the rest of the line into value
while (map_file >> key && getline(map_file, value))
if (value.size() > 1) // check that there is a transformation
trans_map[key] = value.substr(1); // skip leading space
else throw runtime_error("no rule for " + key);
return trans_map;
}
8:12:30
map_file(意思就是給建置map用檔案)中的每一行都是對應到轉換憑據檔的每一條規則,而每一條規則都是一個文字、一個半形空格,再接要轉成的片語(片語中當然可能會有半形空格存在)。我們用移位(shift)運算子「>>」來讀取規則裡頭那個要被轉換的文字,並存到變數key中,作為trans_map的鍵值備用,然後調用string的getline函式來讀取同一行中的其餘部分,以存到變數value中。因為getline並不會自己略去開頭的那個半形空格(§3.2.2,頁87),所以我們就必須自己將這個半形空格給略去。在我們儲存每一條轉換規則前,也應該先檢查看看,這個value現有的值,是否較一個半形空格還長;如果能較一個半形空格還長的話,才調用substr成員函式(§9.5.1,頁361)來略過那個半形空格,並將這個substr回傳的值當作「值」,存到trans_map中。
請注意我們這裡是用下標運算子來直接新增元素到map;也就是說,如果轉換規則中有一個字是對應到多個規則,那麼我們將會逕行忽略,且只保留該字對應到的最後一條規則。當這個while迴圈結束時,trans_map就會是一個我們據以轉換文字的map。
Note that we use the subscript operator to add the key–value pairs. Implicitly, we are ignoring what should happen if a word appears more than once in our transformation file. If a word does appear multiple times, our loops will put the last corresponding phrase into trans_map.
Implicitly,中文語境要翻成這樣→我們這麼做就是想要忽略……怎麼可以照英文直譯,成了洋涇濱中文?!8:47:20
隱含地,我們忽略了一個字詞在我們的變換檔案中出現一次以上的情況。
要這樣翻,根本叫機器翻譯就好啦。又,可以用「變換」來修飾「檔案」嗎?這種中文程度!
中文版這裡翻得有夠爛!下面title竟然翻成「產生一個變換」!
Generating a Transformation 取得轉換結果
實際執行文字轉換工作的則是transform函式。它的參數是兩個參考,第1個是對要轉換文字的string型別的參考,另一個則是對轉換文字用的map的參考【意即都是用傳址(pass by reference)的方式傳遞所需資料】。如果要轉的文字出現在轉換憑據用的map中,那麼這個transform函式就會回傳在map中對應到的轉換結果。如果不是這樣,那麼函式就回傳它的第1個引數【引渡給它來計算的資料、數據】,也就是那個string:
頁443
9:2:30
const sting &transform(const string &s, const map<string, string> &m)
{
//map容器在這個程式中實際的作用;這也就是整個程式的核心(heart中樞)部分
auto map_it = m.find(s);
//如果在map中找到要轉換的文字
if (map_it != m.cend())
return map_it->second; //就用轉換後的片語(或文字)來回傳
else
return s; //若找不到轉換規則就將s原封不動地回傳
}
const string & transform(const string &s, const map<string, string> &m)
{
// the actual map work; this part is the heart of the program
auto map_it = m.find(s);
// if this word is in the transformation map
if (map_it != m.cend())
return map_it->second; // use the replacement word
else
return s; // otherwise return the original unchanged
}
9:21:00
這裡是先用find來判斷待轉的文字是否存在於map中。若是,find回傳的就會是一個指向該元素的迭代器,否則就會回傳一個尾端後迭代器(off-the-end iterator)。如果不是個尾端後迭代器,那麼就將之解參考以取得該元素,因為map元素的型別是pair(§11.3,頁428),所以回傳pair的second成員,就是s轉換後的文字或片語。
we dereference the iterator, obtaining a pair that holds the key and value for that element (§ 11.3, p. 428).
We return the second member, which is the transformation to use in place of s.
練習11.33
9:26:10
寫一個妳自己的文字轉換程式。
Implement your own version of the word-transformation program.
參見練習11.38
#include<iostream>
#include<fstream>
#include<iterator>
#include<map>
using namespace std;
int main() {
map<string, string>mReplace;
ifstream ifs("V:\\Programming\\C++\\OCRtxtCorrect1.txt");
ifstream ifsInput("V:\\Programming\\C++\\input1.txt");
istream_iterator<string>in(ifs), end;
string key, value;
while (in != end) {
key = *in; value = *++in;
mReplace.insert(make_pair(key, value)); ++in;
}
istream_iterator<string>input(ifsInput), e;
map<string, string>::const_iterator mIt;
string ocr;
while (input != e)
{
ocr = *input;
mIt = mReplace.find(ocr);
if (mIt != mReplace.cend())
cout << mIt->second << endl;
else
cout << ocr << endl;
++input;
}
}
練習11.34
如果我們在前述程式碼的transform函式中改用下標運算子而不是find的話會發生什麼事情?
9:32:50
如果用下標則會在找不到時,改變map大小,多加了一個元素進去;若無指定轉換之規則,會在轉換憑據的map中產生一個待轉文字轉成空字串的新規則,因為該規則的string會被預設初始化為空字串。
#include<iostream>
#include<fstream>
#include<sstream>
#include<iterator>
#include<map>
#include<string>
using namespace std;
int main() {
map<string, string>mReplace;
ifstream ifs("V:\\Programming\\C++\\exercise11_34transformRule.txt");
ifstream ifsInput("V:\\Programming\\C++\\exercise11_34input.txt");
istream_iterator<string>in(ifs), end;
string key, value;
while (in != end) {
key = *in; getline(ifs, value);
if (value.size() > 1)
mReplace.insert(make_pair(key, value.substr(1)));
else
throw runtime_error("沒有轉換規則!");
++in;
}
map<string, string>::const_iterator mIt;
string ocr, transformedLine;
while (getline(ifsInput, transformedLine)) {
istringstream iss(transformedLine);
istream_iterator<string>input(iss), e, wPos;
bool wLast = false;
while (input != e)
{
ocr = *input;
wPos = ++input;
if (wPos == e) wLast = true;
mIt = mReplace.find(ocr);
if (mIt != mReplace.cend())
cout << mIt->second << ((wLast) ? "" : " ");//利用條件運算子(頁151)判斷是否是本行最後1個字,是,就不再後綴半形空格
else
cout << ocr << ((wLast) ? "" : " ");
//cout << mReplace[ocr] << ((wLast)?"":" ");//下標運算子
/*用下標運算子的結果:
are you
why you picture
okay? thanks! later
原本規則沒有字就會被取代為空字串*/
}
cout << endl;
}
}
練習11.35
10:48:10
將函式buildMap的「trans_map[key]=value.substr(1);」改寫成以下這樣,可能會有什麼影響?
trans_map.insert({key,value.substr(1)})
英文版原文排版格式錯誤。「as」和「?」都該如正文字體:
Exercise 11.35: In buildMap, what effect, if any, would there be from
rewriting
trans_map[key] = value.substr(1);
as trans_map.insert({key, value.substr(1)})?
原式以下標運算者,若已有欲轉換文字存在map中,則後來再插入其文字的規則會取代之前的。而用insert就不會取代之前的。因為「對map和set來說,只有在其原來並不具有該元素的前提下才會再插入或建構元素給它們(這裡是c這個容器)」。
下標(subscript)者,會改動已有key的value值,而插入insert()者不會(即若鍵值已存在,則不會再插入!)
https://github.com/oscarsun72/prog1-C-Primer-5th-Edition-s-Exercises/blob/exercise11_35/prog1/prog1.cpp
https://github.com/oscarsun72/prog1-C-Primer-5th-Edition-s-Exercises/blob/exercise11_35revise/prog1/prog1.cpp
練習11.36
我們這個文字轉換程式並沒有設置檢查來源檔案有效性的機制。尤其是,我們的設計是基於:在轉換憑據的檔案中,所有的轉換規則都是正確的,這一點來做的。如果在該檔案有一條規則是有待轉字,後也接了一個半形空格,可是缺了轉成的片語字串,那麼會發生什麼事?想像一下,然後根據妳11.33練習寫的版本來測試看看,是否如妳所想。
11:43:00
中文版又翻錯:
What would happen if a line in that file has a key, one space,
and then the end of the line?
如果那個檔案中有一行只有一個鍵值、一個空格,然後就是檔案結尾,那會發生什麼事?
11:22:00
課本的版本就是buildMap函式的定義中的這行會執行:11:50:00
throw runtime_error("no rule for " + key);
Unhandled exception at 0x00007FFB56CAA839 in prog1.exe: Microsoft C++ exception: std::runtime_error at memory location 0x00000047DC6FFA88. occurred
我的版本則是會停止再新增map元素!!
最後一個元素便不是合法的元素值便不會被加入map
2:53:20
C++中容器總共有3大類(一如迭代器有5大類的大類),其共通性為下標與取得其對應值這樣的結構,下標的運算元都是不能動的!:(鍵值就是索引值,就是定位點定位用的點)
1.array、string、vector……等循序容器:下標用的運算元就是索引值功能就同鍵值,也是唯讀的(1就是1的位置,不可能是2,諸如此類等等),一如鍵值也是用來下標,且是唯讀的。下標是為了取得元素「值」,因其元素型別不是鍵值與值對組(key-value pairs),所以其元素值,就是其鍵值與值對組「值」。
2.map:下標的就是鍵值,也是唯讀的。下標也是為了取得元素「值」。這是最正常的【下標取值】的結構。
3.set:下標(鍵值)就是「值」。可以說set的鍵值是隱藏版的!
可見這3大類其實就是一種的變種而已。都是以「鍵值與值對組」這概念來引申的,都是為了取得「值」而作下標的運算。只不過第1種是用(正整數含0的)數字來定位,後2種則是用某個值來作定位。
留言