35-使用STL
16.6.5 使用STL
STL是一个库,其组成部分被设计成协同工作。STL组件是工具,但也是创建其他工具的基本部件。我们用一个例子说明。假设要编写一个程序,让用户输入单词。希望最后得到一个按输入顺序排列的单词列表、一个按字母顺序排列的单词列表(忽略大小写),并记录每个单词被输入的次数。出于简化的目的,假设输入中不包含数字和标点符号。
输入和保存单词列表很简单。可以按程序清单16.8和程序清单16.9那样创建一个vector
vector<string> words;
string input;
while (cin >> input && input != "quit")
words.push_back(input);
如何得到按字母顺序排列的单词列表呢?可以使用sort(),然后使用unique(),但这种方法将覆盖原始数据,因为sort()是就地算法。有一种更简单的方法,可以避免这种问题:创建一个set
set<string> wordset;
transform(words.begin(), words.end(),
insert_iterator<set<string> > (wordset, wordset.begin()), ToLower);
ToLower()函数很容易编写,只需使用transform()将tolower()函数应用于字符串中的各个元素,并将字符串用作源和目标。记住,string对象也可以使用STL函数。将字符串按引用传递和返回意味着算法不必复制字符串,而可以直接操作原始字符串。下面是函数ToLower()的代码:
string & ToLower(string & st)
{
transform(st.begin(), st.end(), st.begin(), tolower);
return st;
}
一个可能出现的问题是:tolower()函数被定义为int tolower(int),而一些编译器希望函数与元素类型(即char)匹配。一种解决方法是,使用toLower代替tolower,并提供下面的定义:
char toLower(char ch) { return tolower(ch); }
要获得每个单词在输入中出现的次数,可以使用count()函数。它将一个区间和一个值作为参数,并返回这个值在区间中出现的次数。可以使用vector对象来提供区间,并使用set对象来提供要计算其出现次数的单词列表。即对于集合中的每个词,都计算它在矢量中出现的次数。要将单词与其出现的次数关联起来,可将单词和计数作为pair<const string, int>对象存储在map对象中。单词将作为键(只出现一次),计数作为值。这可以通过一个循环来完成:
map<string, int> wordmap;
set<string>::iterator si;
for (si = wordset.begin(); si != wordset.end(); si++)
wordmap.insert(pair<string, int>(*si, count(words.begin(),
words.end(), *si)));
map类有一个有趣的特征:可以用数组表示法(将键用作索引)来访问存储的值。例如,wordmap[“the”]表示与键“the”相关联的值,这里是字符串“the”出现的次数。因为wordset容器保存了wordmap使用的全部键,所以可以用下面的代码来存储结果,这是一种更具吸引力的方法:
for (si = wordset.begin(); si != wordset.end(); si++)
wordmap[*si] = count(words.begin(), words.end(), *si);
因为si指向wordset容器中的一个字符串,所以*si是一个字符串,可以用作wordmap的键。上述代码将键和值都放到wordmap映象中。
同样,也可以使用数组表示法来报告结果:
for (si = wordset.begin(); si != wordset.end(); si++)
cout << *si << ": " << wordmap[*si] << endl;
如果键无效,则对应的值将为0。
程序清单16.19把这些想法组合在一起,同时包含了用于显示3个容器(包含输入内容的矢量、包含单词列表的集合和包含单词计数的映象)内容的代码。
程序清单16.19 usealgo.cpp
//usealgo.cpp -- using several STL elements
#include <iostream>
#include <string>
#include <vector>
#include <set>
#include <map>
#include <iterator>
#include <algorithm>
#include <cctype>
using namespace std;
char toLower(char ch) { return tolower(ch); }
string & ToLower(string & st);
void display(const string & s);
int main()
{
vector<string> words;
cout << "Enter words (enter quit to quit):\n";
string input;
while (cin >> input && input != "quit")
words.push_back(input);
cout << "You entered the following words:\n";
for_each(words.begin(), words.end(), display);
cout << endl;
// place words in set, converting to lowercase
set<string> wordset;
transform(words.begin(), words.end(),
insert_iterator<set<string> > (wordset, wordset.begin()),
ToLower);
cout << "\nAlphabetic list of words:\n";
for_each(wordset.begin(), wordset.end(), display);
cout << endl;
// place word and frequency in map
map<string, int> wordmap;
set<string>::iterator si;
for (si = wordset.begin(); si != wordset.end(); si++)
wordmap[*si] = count(words.begin(), words.end(), *si);
// display map contents
cout << "\nWord frequency:\n";
for (si = wordset.begin(); si != wordset.end(); si++)
cout << *si << ": " << wordmap[*si] << endl;
return 0;
}
string & ToLower(string & st)
{
transform(st.begin(), st.end(), st.begin(), toLower);
return st;
}
void display(const string & s)
{
cout << s << " ";
}
程序清单16.19中程序的运行情况如下:
Enter words (enter quit to quit):
The dog saw the cat and thought the cat fat
The cat thought the cat perfect
quit
You entered the following words:
The dog saw the cat and thought the cat fat The cat thought the cat perfect
Alphabetic list of words:
and cat dog fat perfect saw the thought
Word frequency:
and: 1
cat: 4
dog: 1
fat: 1
perfect: 1
saw: 1
the: 5
thought: 2
这里的寓意在于,使用STL时应尽可能减少要编写的代码。STL通用、灵活的设计将节省大量工作。另外,STL设计者就是非常关心效率的算法人员,算法是经过仔细选择的,并且是内联的。