第六十四讲 初识标准库(3)

我们剩下的内容不多了,不过这一讲的内容要稍微难理解一些,其实也不难了,我们现在来看看剩下的内容:
—————————–
5)再按照长短排序。
6)计算超过某个数字的单词个数。
7)去掉任何没有语义的中性词(and,if,or,but等等)。
8)打印出最后所剩下的。

————————–

我们今天的内容就是要完成上面的任务,其实至于第八条我们一开始就完成了,所以说到底,今天其实,我们只完成三条而已,现在我们来看看,怎么能够按照长度排序。

上一讲,我们说到sort,sort默认情况下是按照字典序排序,当然我们可以给他第三个参数,第三个参数是个比较器,到底叫不叫比较器我不是太明白,因为关于英语的翻译不同的人有不同的理解,所以我把他叫做一个比较器,所以,为了按长短排序,我们得写给比较函数:
——————————-
bool less_than(const string&

str1, const string&

str2){

return str1.size() <str2.size();


}
——————————-
现在我们可以将这个比较函数的地址传递给sort,如下:
——————————-
sort(it,Array_Text.end(),less_than);


——————————–
不过我们会发现,当我们使用了这个sort那字数排序后又带来了另一个问题,因为他忽略了我们早些时候辛苦弄的字典排序却被打乱了,没关系,标准库里还有另一个排序函数,叫做stable_sort(),这个函数用法和sort一样,但是他有个好处,他可以保持预先我们拍好的字典序的相对位置不变,所以,我们将函数修改如下:
——————————-
stable_sort(it, Array_Text.end(), less_than);


——————————-

嗯,关于第五点,似乎我们是完成了,不过效率却不高,每次比较都会寻址然后把less_than作为单条语句来实现,我们原本期望是让他成为inline展开,但是函数指针却无法做到inline,那么怎么才能做到inline呢?这里我们得引入另一个概念——函数对象。

函数对象,第一次接触这个名词的时候总觉得怪怪的,不知道大家是不是,反正我当时就是这么想的,怎么函数也成对象了,后来明白了,其实这不是啥函数,而是一个class,因为只有class才能够拥有对象,所以我们所说的函数对象其实就是一个重载()操作符的一个class,明白函数对象后我们就可以写这么一个class出来:
——————————–
class Less_Than{
public:

bool operator()(const string&

str1, const string&

str2){

return str1.size() <str2.size();


}
};


——————————–
有了这个后,我们将上面的stable_sort()修改一下:
——————————
stable_sort(it, Array_Text.end(), Less_Than());


——————————-
注意到了吗?我们函数对象用的是Less_Than(),不要忽略了后面的括号,因为这个括号就是我们重载的(),所以他完成了我们需要的inline,现在我们的stable_sort就可以按内联展开了,于是乎效率就哗啦啦的上去了。
好吧,第五条现在算是完成了,我们可以继续来看看下一条:
——————————-
6)计算超过某个数字的单词个数。
——————————-
关于这一点不难,因为标准库里面有个叫count_if()的函数,这个函数就是为了计算满足某些条件的元素个数,这一点可能我的表达能力不是太强,不过希望大家能够明白我所说的是什么,简单点说,我们就是用这个函数去计算超过某个个数的单词的个数,这一个函数同样需要三个参数,这三个参数和stable_sort的一样,所以,同样我们也得需要一个函数对象(为了效率):
———————————–
class GreaterThan{
public:

GreaterThan(int
n = 0) :m_Count(n){ }

int size(){

return m_Count;

}

bool operator()(const string&

str1){

return str1.size()>m_Count;


}
private:

int m_Count;


};


————————————-
现在我们可以这样使用这个函数对象了:
————————————-
int count = count_if(it, Array_Text.end(), GreaterThan());


————————————-
当然这是默认情况了,而默认情况下我们函数对象的内部数据保存的数据是0,所以上面这条语句其实会计算出容器中的所有元素个数,不过没关系,如果我们想要计算出字母个数大于5的单词数,我们只需要用GreaterThan(5)即可,所以这一条也算是完成了,那么我们再看看接下来的最后一条:
—————————————
7)去掉任何没有语义的中性词(and,if,or,but等等)
—————————————

如果我们要从一个容器中移除这些无意义的单词,我们要用一个remove的算法,remove和unique一样,他并不会改变容器的大小,而是把需要保留的放在前面,把不需要的扔在后面,同时返回一个指向不需要的第一个元素,所以想要真正的改变容器大小,我们还得使用erase()(其实erase并不会真正的修改容器大小,他只是真正的删除元素,要想真正的修改容器大小,回收内存,还得靠swap,当然我们再来讨论这个问题),好吧,我们把需要删除的单词放在一个数组里,然后再把他放进一个vector里面,再来进入remove,如下:
———————————————–
string rm[] ={ "

and"

, "

or"

, "

if"

, "

the"

, "

but"

};


my_Text remove_vector(rm, rm + 5);


my_Text::iterator remove_it = remove_vector.begin();



for (;

remove_it != remove_vector.end();

++remove_it){

Array_Text.erase(

remove(Array_Text.begin(),

Array_Text.end(), *remove_it),

Array_Text.end());


}
———————————————–

现在我们的任务基本完成了,不过既然这里我们说标准库,我们我们就再来看看最后一点,打印所有的元素:
——————————-
8)打印出最后所剩下的。
——————————-
通常情况下,我们会用下面的书写方式:
—————————————-
while (it != Array_Text.end())

outFile <<*it++ <<"

"

;


—————————————-

不过标准库有个通用算法,for_each(),所以现在我们来尝试使用这个函数,不过这个函数和sort和count_if一样,同样需要一个函数指针或者是一个函数对象来作为for_each的第三个参数,我们需要用for_each来打印元素,所以我们可以来写一个打印的函数:
—————————————-

void putstring(const string&

str1){

cout <<str1 <<"

"

;


}
—————————————-

然后我们就可以直接使用for_each():
—————————————-
for_each(it, Array_Text.end(), putstring);


—————————————-

现在一切总算是完成了,看似一个很庞大的问题,我们只是简单的把标准库里面的几个函数串联起来,整个程序一共才用了75行,现在我们算是对标准库有些了解了,所以接下来我们该要细说标准库了,我们从vector开始,当然不是今天。
=============================
回复D直接查看目录

原文始发于微信公众号(

C/C++的编程教室

):第六十四讲 初识标准库(3)

|

发表评论