一个简单的演示
让我们通过一个实例来具体地讨论一下。下面的例子实现了文本词汇统计。它展示了map, hash_set, 和vector的使用。函数声明如下:
map^
build_word_count( vector^>^ words,
array ^common_words ) |
模板语法让人看起来非常地复杂。让我们看看能够做哪些有益的改进。build_word_count() 的返回类型是map,键(key)是字符串—文本中每一个不同于其他词的单词—值(value)是用于统计相关联词出现次数的整数。函数的第一个参数是CLI数组向量(vector),它将文件中的词汇保存为字符串元素。第2个参数中保存着我们要排除在外的字符集。
由于很多“帽子”和右中括号间隔出现,这看起来相当的复杂。我们可以使用typedef从文字上化简一下:
typedef array^ words; typedef vector^ text; |
使用typedef重定义的函数如下所示:
map^ build_word_count( text theText, words common ) |
我们也可以除去显式的map声明,但是出于读者练习的考虑我仍然保留了声明。下一个工作是实现。我们可以将之分为两部分:(1) map和hash_set的初始化,如下所示,(2)实际的文本处理程序。
数组元素中的第一个直至有效的最后一个元素的地址装入hash_set的构造函数为元素装入hash_set提供了同等的机会。这两个地址为hash_set 构造函数的迭代提供了一个元素范围,轮流的将它们插入容器中。这就是我在这部分开始提到的迭代器的范围。
实现的第2部分便是文本处理。因为我们还没有看到官方正式发布的迭代器,我们现在仍然使用for循环来遍历vector。我们将使用for each语句来遍历vector的每一个数组元素。这就是如上所述的代码:
//循环的访问每一个数组
for ( int ix = 0; ix < theText->size(); ++ix ) { for each ( String^ wd in theText[ ix ] ) if ( common->find( wd ) == common->end() ) word_map[ wd ]++; }
// 不要忘记返回结果…… return word_map; |
find() 是hash_set的成员函数,它用于确定词汇项是否已经存在于set当中—所有的关联式容器都支持find()成员函数。(是的,顺序式容器,以及内置数组,和所有你可能将集成到STL/STL.NET模型中的集合,都使find()泛型算法。算法和容器的隔离理论上比实际上要为严重。我们将特别地讨论一下列表顺序式容器(list sequential container) 。)他返回的是我还没有讨论过的—容器中的迭代器。此时,我们将迭代器假想为指针类型。如果在容器中词汇项已经存在了,那么find()将返回给它一个迭代器;否则,将返回最后一个词汇项的迭代器。这与end()返回的迭代器值是相同的。我们判断STL下的元素搜索是否成功的方法就是比较用搜索方法返回的迭代器值与end()方法返回的迭代器值是否相同。如果两者相同,那么元素便不在容器中。
每一个容器都提供begin()和end()成员函数。begin()返回给容器中first元素一个迭代器。正如我前面说到的,end()返回给容器中last元素一个迭代器。举例如下,下面的程序中展示了我们如何声明两个迭代器并且用这两个成员函数初始化它们,
vector::iterator first = theText->begin(); vector::iterator last = theText->end(); |
容器的遍历一般使first在一个for循环或者是while循环中递增,当first等于last的时候终止循环。例如,
for ( ; first != last; ++first ) |
对迭代器对的要求是他们必须能够通过增量运算符的重复应用以first开头并能够达到last。尽管如此,编译器自身不能做到;如果不能够达到这个要求则会产生未定义的运行时行为(undefined run-time behavior)。
我们通过反引用(*)操作符访问迭代器涉及到的元素。例如,为了重新获得我们的向量(vector)的每一个CLI数组元素,我们在上述的for循环体中写入下面的代码,
我们将在这一系列文章的后续文章中介绍关联式容器的声明和用法。