遍历和谓词
迭代器使我们能写出迭代穿过一个序列的循环。当然,总写循环也会使人厌倦,为此,标准库提供了一些能对序列中的每个元素调用一个函数的方法。
考虑去写一个程序,它从输入中读一些单词,并记录下它们出现的频率。表示字符串及其相关频率的最明显方式是采用一个 map
map<string, int> histogram;
对每个串记录其频率,需要做的明显操作是
void record(const string& s)
{
histogram[s]++; // 记录“s”出现的次数
}
一旦读完了输入,我们还可能想要输出工作中收集到的数据。这个 map
由一个 (string,int)
对的序列组成,因此,我们就希望对 map
中的每个元素调用下面函数:
void print(const pair<const string, int>& r)
{
cout << r.first << ' ' << r.second << '\n';
}
( pair
的第一个元素被称为 first
,第二个元素被称为 second
)。pair
的第一个元素是 const string
,而不是普通的 string
,因为 map
里所有的关键码都是常量。
这样,主程序就可以写成:
int main()
{
istream_iterator<string> ii(cin);
istream_iterator<string> eos;
for_each(ii, eos, record); // #include <algorithm>
for_each(histogram.begin(), histogram.end(), print);
}
请注意,我们不需要对 map
排序以便使输出是按顺序的,因为 map
总是按顺序保存它的元素,因此,它的迭代器也将按(上升)序遍历 map
的所有元素。
许多程序设计工作都涉及到在容器中寻找某些东西,而不是简单地对每个元素做某件事情。例如,find
算法(18.5.2节)是为寻找某个特定值提供的一种很方便的方式。这一思想的另一种更一般的变形是寻找满足某种特殊需要的元素。例如,我们可能需要在一个 map
里寻找第一个大于 42
的值。一个 map
就是一个(关键码,值)对偶的序列,因此我们需要检索这个序列,去找一个 pair< const string, int>
,其中的 int
大于 42
:
bool gt_42(const pair<const string, int>& r)
{
return r.senond > 42;
}
void f(map<string, int>& m)
{
typedef map<string,int>::const_iterator M1;
M1 i = find_if(m.begin(), m.end(), gt_42);
// ...
}
换一种情况,我们也可能需要统计频率高于 42
的单词的个数:
void g(const map<string, int>& m)
{
int c42 = count_if(m.begin(), m.end(), gt_42);
// ...
}
像 gt_42()
这样用于控制算法的函数被称为谓词。谓词将针对每个元素调用并返回布尔值,算法根据这种返回值去执行自己预定的动作。例如,find_if()
将检索到它的谓词返回 true
为止,这说明找到了一个要找的元素。与此类似,count_if()
统计出它的谓词返回 true
的次数。
标准库提供了若干很有用的谓词,以及一些能用于构造出更多谓词的模板(18.4.2节)。
🔚