stl算法_STL算法简介

stl算法

Which STL Container? before you continue. 哪个STL容器? 在继续之前。

A Brief Overview of the STL

STL的简要概述

The STL is primarily composed of three different types of entities: containers, algorithms, and iterators.  Containers are written as template classes, and that means they are instantiated using a special argument referred to as a template argument, which is always a data-type (e.g. int, string, float, etc.).  When you create an STL container, the template argument you pass in specifies the type of the data you are holding within the container (syntax: 'list<int> myList' for creating a list container named mylist for holding integers).  The second entity type, algorithms, are functions capable of using and manipulating the data held within STL containers.  Algorithms typically take in a range of container elements specified by iterators and act on them.  Please note that in this article we always act on the whole container by using the iterators returned by the container.begin() and container.end() functions as it makes the examples cleaner.  Lastly, iterators are the mechanisms that allow the safe traversal of all items in STL containers; they are the entity that enables STL algorithms to process STL containers.

STL主要由三种不同类型的实体组成: 容器算法迭代器 。 容器是作为模板类编写的,这意味着它们使用称为模板参数的特殊参数实例化,该参数始终是数据类型(例如,int,string,float等)。 创建STL容器时,传入的模板参数指定容器中保存的数据类型(语法:“ list <int> myList”,用于创建名为mylist的列表容器以保存整数)。 第二种实体类型(算法)是能够使用和操纵STL容器中保存的数据的功能。 算法通常采用迭代器指定的一系列容器元素并对它们进行操作。 请注意,在本文中,我们始终通过使用container.begin()和container.end()函数返回的迭代器来对整个容器进行操作,因为它使示例更简洁。 最后,迭代器是允许安全遍历STL容器中所有项目的机制。 它们是使STL算法能够处理STL容器的实体。

STL algorithms each work on a subset, if not all STL containers.  So, before using a STL algorithm you should ensure that the container(s) you are using that algorithm on provide the correct prerequisites for that algorithm.  For example, the STL sort function requires random-access iterators as input parameters, and STL lists do not provide that kind of iterator so they cannot use the function.  It's worth noting that typically when a container cannot be used by an important algorithm like sort, that container usually provides a member function to accomplish the same action in a way suitable for that container type (and list::sort is one of these cases).  Lastly, you should also know that sometimes an algorithm cannot be used with a certain container type because that algorithm would cause an invalid state when applied to that container.  A good example of this is how you cannot apply sort to an associative container (set, map) becasue associative containers are sorted by default and changing that sorting would invalidate their internal structure (despite all this criticism, sort is still a wonderful algorithm!).

STL算法每个都在一个子集上运行,即使不是所有STL容器也是如此。 因此,在使用STL算法之前,应确保使用该算法的容器为该算法提供正确的先决条件。 例如,STL排序功能需要随机访问迭代器作为输入参数,而STL列表不提供这种迭代器,因此它们不能使用该函数。 值得注意的是,通常当容器无法被诸如sort之类的重要算法使用时,该容器通常会提供成员函数以适合该容器类型的方式完成相同的操作(而list :: sort是其中一种情况) 。 最后,您还应该知道,有时某个算法不能与某种容器类型一起使用,因为该算法在应用于该容器时会导致无效状态。 一个很好的例子是,如何无法对关联容器(集合,地图)应用排序,因为关联容器默认情况下是排序的,因此更改排序将使它们的内部结构无效(尽管有很多批评,排序仍然是一种出色的算法!) 。

Working Examples

工作实例

The STL has a great number of algorithms built into it; far too many to try and cover in a single article.  This section aims to present some of the more useful algorithms in working, well-commented code.  It is my hope that seeing some of the STL algorithms in action will give you the start or refresher you need to begin delving into the STL yourself.  I would urge you to keep two things in mind when reading this article, though.  First, the STL often provides multiple functions for variations on a single cause, so it’s worth looking into what’s available before using any STL function (e.g. there are many, many ways to find an element or group of elements in a collection).  The other thing to remember is that many STL algorithms are less efficient than the member function algorithms provided by the STL container classes themselves.  So keep in mind that if you see a generic STL algorithm and a member function provided by your STL container with the same name, chances are the member function will accomplish the same task with greater efficiency.  Now let’s start looking at some examples:

STL内置了大量算法。 太多的内容无法尝试在一篇文章中介绍。 本节旨在介绍在运行良好且经过良好注释的代码中一些更有用的算法。 我希望看到一些STL算法在起作用,这将为您提供开始或重新开始自己研究STL所需的开始或复习。 不过,我敦促您在阅读本文时记住两点。 首先,STL通常为单一原因提供多种功能来进行变化,因此在使用任何STL功能之前,有必要先研究可用的功能(例如,有很多很多方法可以在集合中查找一个元素或一组元素)。 要记住的另一件事是,许多STL算法的效率低于STL容器类本身提供的成员函数算法。 因此请记住,如果您看到通用STL算法和STL容器提供的具有相同名称的成员函数,则该成员函数很可能会以更高的效率完成相同的任务。 现在让我们开始看一些例子:

Example #1 - STL Copy Algorithm

I'm starting with the copy algorithm as it is a very useful algorithm which will be applied throughout this article to help output the contents of STL containers quickly.  It will also be used to copy container contents between different types of containers as shown below.  First I will show you the example and then I will explain it in detail afterward.

我从复制算法开始,因为它是一个非常有用的算法,将在本文中应用,以帮助快速输出STL容器的内容。 如下所示,它还将用于在不同类型的容器之间复制容器内容。 首先,我将向您展示该示例,然后再对其进行详细说明。

#include <iostream>
#include <vector>
#include <deque>
#include <iterator>

int main() 
{
    std::cout<<std::endl<<"Creating vector and filling with values 0-9.";
    std::vector<int> intVector;
    for (int i = 0; i < 10; ++i)
        intVector.push_back(i);

    std::cout<<std::endl<<std::endl<<"Using copy function to output vector contents:\n";
    std::copy(intVector.begin(), intVector.end(), std::ostream_iterator<int>(std::cout, " "));

    std::cout<<std::endl<<std::endl<<"Creating a deque and using copy with back_inserter to populate it with vector contents back-first.";
    std::deque<int> intDeque;
    std::copy(intVector.begin(), intVector.end(), back_inserter(intDeque));

    std::cout<<std::endl<<std::endl<<"Using copy with front_inserter to insert vector data to the deque again front-first.";
    std::copy(intVector.begin(), intVector.end(), front_inserter(intDeque));

    std::cout<<std::endl<<std::endl<<"Outputting the contents of the new deque with copy, should be 9..0..0..9:\n";
    std::copy(intDeque.begin(), intDeque.end(), std::ostream_iterator<int>(std::cout, " "));
    
    std::cout<<std::endl<<std::endl; //Formatting
}

The sample code creates a vector of 10 elements initialized with the values 0-9.  The first thing that it does after this is output the contents of the vector using the copy function (this is pretty cool, it saves you a lot of code when used frequently!).  The copy function works by taking in a range of elements specified by iterators; in this case it takes our entire vector since we used the iterators retrieved by .begin() and .end(). It copies that range specified by the first two parameters into the destination specified by the third parameter.  The third parameter can be a number of things, but in this case we have used an ostream_iterator with an integer template parameter to use the cout stream while spacing elements with a delimiter of " ".  The effect of this is basically the same as using a for loop to cycle through the vector contents and output them with cout.

该示例代码创建了一个由10个元素组成的矢量,这些矢量用值0-9初始化。 此后它要做的第一件事是使用复制功能输出向量的内容(这很酷,经常使用它可以为您节省很多代码!)。 复制函数通过吸收迭代器指定的一系列元素来工作; 在这种情况下,由于我们使用了.begin()和.end()检索到的迭代器,因此它占用了整个矢量。 它将前两个参数指定的范围复制到第三个参数指定的目标。 第三个参数可以是很多东西,但是在这种情况下,我们使用了带有整数模板参数的ostream_iterator来使用cout流,同时使用分隔符“”分隔元素。 这样做的效果基本上与使用for循环遍历向量内容并使用cout输出它们相同。

Next, we create an empty deque and call copy twice with the full range of vector elements using the deque as the destination parameter.  In the first call to copy, we use back_inserter(deque) to insert each element in the range into the back of the deque one at a time.  In the second call, we do the same thing with front_inserter(deque) to insert each element in the range to the front of the deque.  The effect of this will be that the deque now holds the vector contents twice in reverse order of each other (i.e. 9,8,7...0...0...7,8,9).  We then use our first form of copy one last time to output the final contents of the deque.

接下来,我们创建一个空的双端队列,并使用该双端队列作为目标参数,使用整个向量元素调用两次复制。 在第一个复制调用中,我们使用back_inserter(deque)将范围内的每个元素一次插入到双端队列的后面。 在第二个调用中,我们使用front_inserter(deque)进行相同的操作,以将范围内的每个元素插入到双端队列的前面。 这样的效果是,双端队列现在以彼此相反的顺序两次保存矢量内容(即9,8,7 ... 0 ... 0 ... 7,8,9)。 然后,我们最后一次使用我们的第一种副本形式输出双端队列的最终内容。

Please note that not all containers support back_inserter and front_inserter.  I used a deque because it does support both and I wanted to show you that different mechanisms could be used within the copy function.

请注意,并非所有容器都支持back_inserter和front_inserter。 我使用了双端队列,因为它同时支持这两种情况,并且我想向您展示在复制功能中可以使用不同的机制。

Example #2 – STL Swap Algorithm

The next code sample will display the use of the swap algorithm (stl::swap and stl::map::swap).  It will use map containers as I want this article to demonstrate to you that STL algorithms can be used on all STL containers.  Again, the detailed description will follow the code sample below.  Please note that we start by extending the standard namespace so that the copy funciton may be used in order to output the contents of our maps.  This will not be required for the rest of the STL containers used in this article.

下一个代码示例将显示交换算法的使用(stl :: swap和stl :: map :: swap)。 正如我希望本文向您演示的那样,它将使用地图容器向您证明STL算法可以在所有STL容器上使用。 同样,详细说明将遵循下面的代码示例。 请注意,我们首先扩展标准名称空间,以便可以使用复制功能来输出地图内容。 对于本文中使用的其余STL容器,则不需要这样做。

#include <iostream>
#include <map>
#include <iterator>

namespace std // extend the std namespace (the magic of ADL means this will be used by copy)
{
    template<typename K, typename V>
    ostream & operator << (ostream & os, std::pair<K, V> const & rhs)
    {
        return os << rhs.first << " => " << boolalpha << rhs.second;
    }
}

int main() 
{
    std::cout<<"Creating myMap as map<int, bool> and filling with {key: int 0-9, value: key%3 > 0 -->bool}.\n";
    std::map<int, bool> myMap;
    for (int i = 0; i < 10; ++i)
        myMap.insert(std::make_pair(i, i%3 > 0));
    
    std::cout<<std::endl<<"Creating myOtherMap and swapping its empty data with myMap using std::swap(map1, map2)...\n";
    std::map<int, bool> myOtherMap;
    std::swap(myMap, myOtherMap);
    
    std::cout<<std::endl<<"Outputting contents of MyMap:\n";    
    std::copy(myMap.begin(), myMap.end(), std::ostream_iterator<std::pair<int, bool> >(std::cout, "\n"));
    std::cout<<std::endl<<"Outputting contents of MyOtherMap:\n"; 
    std::copy(myOtherMap.begin(), myOtherMap.end(), std::ostream_iterator<std::pair<int, bool> >(std::cout, "\n"));    
    
    std::cout << std::endl << "Swapping myMap data back into myOtherMap using std::map::swap(map2)...\n";
    myOtherMap.swap(myMap);

    std::cout<<std::endl<<"Outputting contents of MyMap:\n";    
    std::copy(myMap.begin(), myMap.end(), std::ostream_iterator<std::pair<int, bool> >(std::cout, "\n"));
    std::cout<<std::endl<<"Outputting contents of MyOtherMap:\n"; 
    std::copy(myOtherMap.begin(), myOtherMap.end(), std::ostream_iterator<std::pair<int, bool> >(std::cout, "\n")); 
    
    std::cout<<std::endl<<std::endl; //Formatting
}

In our main function we start by creating a map that uses a key type of int and a value type of bool.  It is populated with the keys 0-9 and values of key%3 (i.e. false if key%3 evaluates to 0, and true otherwise).  This isn't that important, but it was just an easy way to create interesting data for the example.  Also, in case you don’t know, values have to be inserted into maps as pairs, and the make_pair function is just an easy way of making those pairs.

在我们的主要功能中,我们首先创建一个映射,该映射使用int的键类型和bool的值类型。 它由键0-9和键%3的值填充(即,如果键%3的值为0,则为false,否则为true)。 这不是那么重要,但这只是为示例创建有趣数据的一种简单方法。 另外,如果您不知道,则必须将值作为对插入到映射中,并且make_pair函数只是建立这些对的一种简单方法。

Moving on, we create another map of the same type and leave it holding no values.  We then use std::swap to swap the values of the first map with the new map.  The values contained in each map are subsequently outputted, and the second map does in fact contain all of the values initially entered into the first map, and the first map is now empty.

继续,我们创建另一个相同类型的地图,并使其不保留任何值。 然后,我们使用std :: swap将第一张地图的值与新地图交换。 每个映射中包含的值随后被输出,第二个映射实际上包含了最初输入到第一个映射中的所有值,并且第一个映射现在为空。

Lastly, we swap the container contents again using std::map::swap this time.  The container contents are outputted in the same manner.  The reason that I've shown you this is that most STL containers provide their own versions of many STL algorithms which are more efficient than the global STL algorithms themselves.  This is a very good example of that point.  Each container in the STL implements its own swap function because swap is a low-level function used in many algorithms and it greatly improves efficiency to use the optimized versions of the function wherever possible.

最后,这次我们再次使用std :: map :: swap交换容器内容。 容器内容物以相同的方式输出。 之所以向您展示这一点,是因为大多数STL容器都提供了自己的许多STL算法版本,这些版本比全局STL算法本身更有效。 这是一个很好的例子。 STL中的每个容器都实现其自己的交换功能,因为交换是许多算法中使用的低级功能,并且它极大地提高了尽可能使用该函数的优化版本的效率。

Example #3 – STL Transform Algorithm and Functor Use

The next example will demonstrate the use of the transform algorithm which can be used to alter the contents of a container using a functor/function object before outputting the results to a desired container.  A function object is an object (i.e. class/struct) that is created to act like a function by having an overloaded () function operator to execute the desired functionality.  This is a very useful thing to know, but I’m not going to go into more detail on the subject.  Please reference this article if you wish to read more on functors. Now, lets move on to the code sample:

下一个示例将演示变换算法的用法,该变换算法可在将结果输出到所需容器之前使用函子/函数对象来更改容器的内容。 函数对象是通过重载()函数运算符执行所需功能而创建的功能类似于函数的对象(即类/结构)。 要知道这是非常有用的事情,但是我不会在这个问题上做更多的详细说明。 如果您想阅读更多关于函子的信息,请参考本文 。 现在,让我们转到代码示例:

#include <iostream>
#include <list>
#include <iterator>
#include <algorithm>

//Functor for the transform_example.
class float_div7_plus2 {
    public:
        float operator()(float val) {
            return val/7+2;
        }
};

int main()
{
    std::list<float> floatList;
    for (int i = 2; i <= 16; i+=2)
        floatList.push_back((float)i);

    std::cout<<"Displaying values held in list before transform:\n";
    std::copy(floatList.begin(), floatList.end(), std::ostream_iterator<float>(std::cout, " "));

    std::cout<<std::endl<<std::endl<<"Transforming numbers by multiplying them by themselves (squaring them).\n";
    std::transform(floatList.begin(), floatList.end(), floatList.begin(), floatList.begin(), std::multiplies<float>());

    std::cout<<"Displaying values held in list after transform squaring operation:\n";
    std::copy(floatList.begin(), floatList.end(), std::ostream_iterator<float>(std::cout, " "));

    std::cout<<std::endl<<std::endl<<"Transforming numbers using custom function to divide by 7 and add 2.\n";
    std::transform(floatList.begin(), floatList.end(), floatList.begin(), float_div7_plus2());
    
    std::cout<<"Displaying values held in list after transform squaring operation:\n";
    std::copy(floatList.begin(), floatList.end(), std::ostream_iterator<float>(std::cout, " "));

    std::cout<<std::endl<<std::endl; //Formatting
}

Our example starts by creating a vector of floats initialized with the values 2-16 counting up by 2.  The contents are outputted using the copy function as you should be getting accustomed to.  After this, we see our first call to transform which looks a little daunting, but it’s really not that bad!  The function call takes the normal first two parameters indicating the range we wish to act on in the source container, and as usual it’s the whole thing.  The third and fourth parameters indicate the container we wish to transform against and the container in which we wish to write our results respectively.  Only start iterators are required for those as we will by default use the same number of elements in those as we do in the source container.  The last parameter is the function object that was mentioned earlier.  There are a number of commonly required function objects built into the STL and this is a good example.  Unsurprisingly, this one causes the elements of our source container to be multiplied against the elements of our second container (which is the same container!).  We then write the results to the source container again; so what we’ve actually managed to do is square all elements in our container in a single line of code.  Not bad, is it!? Please note though that you cannot allow the source and destination iterators to cross over when acting on the same container; if they do then the result will be undefined.  It may be a better idea to look into the std::for_each algorithm when writing results to the source container outside this article.

我们的示例从创建一个浮点向量开始,该向量的初始值是2-16,以2递增。根据您的习惯,将使用copy函数输出内容。 在此之后,我们看到了第一个转换请求,看起来有些令人生畏,但实际上还不错! 函数调用采用通常的前两个参数来指示我们希望在源容器中作用的范围,并且通常情况就是如此。 第三个和第四个参数分别指示我们要转换的容器和希望在其中写入结果的容器。 那些仅需要启动迭代器,因为默认情况下,我们将在源容器中使用相同数量的元素。 最后一个参数是前面提到的功能对象。 STL内置了许多通常需要的功能对象,这是一个很好的例子。 毫不奇怪,这会使我们的源容器的元素与第二个容器(即同一容器!)的元素相乘。 然后,我们将结果再次写入源容器。 因此,我们实际上设法将容器中的所有元素都放在一行代码中。 还不错,是!! 请注意,尽管在同一容器上执行操作时不能允许源迭代器和目标迭代器交叉; 如果这样做,结果将是不确定的。 将结果写入本文之外的源容器时,最好考虑一下std :: for_each算法。

Moving on, we output the results before calling another transform function which takes all of the same parameters with the exception of the functor.  This time we’ll use a user-defined functor which divides the element of the container by 7 and adds 2.  This is just a random decision, the functor could do anything it wanted; it could even save state and perform some complex operations if we wanted.  You can see how easy it is to dramatically change the functionality of an STL algorithm with powerful features like functors (and user-defined predicates as you’ll see later).

继续,我们在调用另一个变换函数之前输出结果,该变换函数采用除函子之外的所有相同参数。 这次,我们将使用用户定义的仿函数,该仿函数将容器的元素除以7并加2。这只是一个随机决定,仿函数可以做任何想要的事情; 如果需要,它甚至可以保存状态并执行一些复杂的操作。 您会发现,使用函子(以及稍后将看到的用户定义的谓词)等强大功能极大地改变STL算法的功能是多么容易。

Example #4 – STL Sort Algorithm

Now let’s try something you’ll definitely find useful... the STL sort algorithm!  Before seeing this example, I would like to remind you of something that I said earlier: the container-specific implementation of functions is often more efficient than the global implementation, and this is a global function as all of the algorithms in this article have been.  The tricks used in this sort example are equally applicable to the container specific functions and can even be used to provide specialized sorting criteria to associative containers like sets and maps within their constructors (though please note that you can't call sort on an associative container itself as its contents are sorted automatically and doing so would damage the container's funcitonality!).  Now, take a look at the example:

现在让我们尝试一些您肯定会发现有用的东西……STL 排序算法 ! 在看到该示例之前,我想提醒您我之前说过的事情:特定于容器的函数的实现通常比全局实现更有效,并且这是全局函数,因为本文中的所有算法都是。 此排序示例中使用的技巧同样适用于特定于容器的功能,甚至可以用于为关联容器(如构造函数中的集合和映射)提供专门的排序条件(尽管请注意,您不能在关联容器上调用sort本身,因为它的内容是自动排序的,这样做会损坏容器的功能!)。 现在,看一下示例:

#include <iostream>
#include <vector>
#include <iterator>
#include <algorithm>

//Binary predicate to compare numbers by the value of their last digit.
bool SortByLastDigit(const int& lhs, const int& rhs) {
    return lhs%10 < rhs%10;
}

int main()
{
    std::cout<<"Creating a vector of integers  that are not properly sorted using (i%3 + (i+2)).\n";
    std::vector<int> intList;
    for (int i = 0; i < 10; ++i)
        intList.push_back(i%3 + (i+2));
    
    std::cout<<"Displaying values held in vector before sort:\n";
    std::copy(intList.begin(), intList.end(), std::ostream_iterator<int>(std::cout, " "));
    
    std::cout<<std::endl<<std::endl<<"Sorting numbers without providing binary predicate --> uses default of <.\n";
    std::sort(intList.begin(), intList.end());
    
    std::cout<<"Displaying sorted results:\n";
    std::copy(intList.begin(), intList.end(), std::ostream_iterator<int>(std::cout, " ")); 
    
    std::cout<<std::endl<<std::endl<<"Sorting contents of vector using SortByLastDigit binary predicate function.\n";
    std::sort(intList.begin(), intList.end(), SortByLastDigit);
    
    std::cout<<"Displaying values of list after SortByLastDigit sort:\n";
    std::copy(intList.begin(), intList.end(), std::ostream_iterator<int>(std::cout, " ")); 
    
    std::cout<<std::endl<<std::endl; //Formatting
}

As usual, we start by creating some sample values in a vector.  Our vector will hold integers which are created using a modulus function so that they are not in order to start with.  We output the contents of the list using copy and move on to call our first sort function.  The sort function you see here, with two parameters, will call the default sort operation on the container which is always less-than (<).  So, our integer vector will come out in perfect order as you see in the next output statement.  

与往常一样,我们从在向量中创建一些样本值开始。 我们的向量将保存使用模数函数创建的整数,这样它们就不会以其开头。 我们使用copy输出列表的内容,然后继续调用我们的第一个sort函数。 您在此处看到的带有两个参数的sort函数将调用容器上的默认排序操作,该操作始终小于(<)。 因此,正如您在下一条输出语句中看到的那样,我们的整数向量将以完美的顺序出现。

This is useful to know, but what’s much more interesting is the fact that we can provide something called a binary predicate on the fly to sort our container in any way we like; we do this in the next three-parameter sort call.  A binary predicate is simply a fancy way of saying a function that takes in two parameters (elements of our container) and returns true or false.  In this case, our binary predicate is called SortByLastDigit, and it returns true if the left-hand parameter provided is less than the right-hand parameter provided.  This is a standard layout for coparison functions which is essentially what the binary predicate is.  The example finishes by showing the output as usual.

知道这很有用,但是更有趣的是,我们可以即时提供一种称为二进制谓词的东西,以便以我们喜欢的任何方式对容器进行排序。 我们将在下一个三参数排序调用中执行此操作。 二进制谓词只是一种表达函数的幻想方法,该函数接受两个参数(容器元素)并返回true或false。 在这种情况下,我们的二进制谓词称为SortByLastDigit,如果提供的左手参数小于提供的右手参数,则它返回true。 这是比较功能的标准布局,本质上是二进制谓词。 该示例通过像往常一样显示输出结束。

Example #5 – STL Search and Replace Algorithms

You should be getting the hang of things now.  This next example will demonstrate how you can locate and alter items using the find_if and replace_if algorithms.  These are just two of the many search and modification functions for STL containers, but they should give you an idea of what’s possible.  Here’s the code:

您现在应该掌握一切。 下一个示例将演示如何使用find_ifreplace_if算法查找和更改项目。 这些只是STL容器的许多搜索和修改功能中的两个,但是它们应该使您了解可能的情况。 这是代码:

#include <iostream>
#include <list>
#include <iterator>
#include <algorithm>

int main()
{
    std::cout<<"Creating intList with values 0-9.\n";
    std::list<int> intList;
    for (int i = 0; i < 10; ++i)
        intList.push_back(i);
    
    std::cout<<"Displaying values held in list before modification:\n";
    std::copy(intList.begin(), intList.end(), std::ostream_iterator<int>(std::cout, " "));
    
    std::cout<<std::endl<<std::endl<<"Using find_if with unary predicate to display odd list items.\n";
    while (true) {
        std::list<int>::iterator found = std::find_if(intList.begin(), intList.end(), std::bind2nd(std::modulus<int>(), 2));    
        if (found != intList.end()) {
            std::cout<<"Found even #: " << *found <<". --> Removing it now"<<std::endl;
            intList.remove(*found);
        } 
        else
            break;
    }
    
    std::cout<<std::endl<<"Displaying values held in list after modification:\n";
    std::copy(intList.begin(), intList.end(), std::ostream_iterator<int>(std::cout, " "));
    
    std::cout<<std::endl<<std::endl<<"Replacing values that are divisible by 4 with 99.\n";
    std::replace_if(intList.begin(), intList.end(), std::bind2nd(std::modulus<int>(), 4), 99);
    
    std::cout<<"Displaying values held in list after modification:\n";
    std::copy(intList.begin(), intList.end(), std::ostream_iterator<int>(std::cout, " "));
    
    std::cout<<std::endl<<std::endl;
}

Again, we start by creating a list of 10 integers 0-9 and outputting it.  We then enter a while loop which will execute forever until all of the odd integers are removed from the list.  In order to locate odd integers, we use the find_if algorithm with a function object created by the call bind2nd(modulus<int>, 2).  

同样,我们从创建10个整数0-9的列表开始并输出它。 然后,我们进入一个while循环,该循环将永远执行,直到从列表中删除所有奇数整数为止。 为了定位奇数整数,我们将find_if算法与通过调用bind2nd(modulus <int>,2)创建的函数对象一起使用。

You will see bind1st and bind2nd frequently in STL, so you should know that they are simply utilities for creating a function object using another function object and a specific parameter.  In this case, we use the modulus function object and bind 2 to its second parameter so that we locate the first container element that doesn’t evaluate to 0 for mod 2 (odd numbers).  The bind1st utility would work similarly in cases that you wanted to bind a specific value to the 1st parameter of the function object in question instead.

您将在STL中经常看到bind1st和bind2nd,因此您应该知道它们只是使用另一个功能对象和特定参数创建功能对象的实用程序。 在这种情况下,我们使用模数函数对象并将2绑定到它的第二个参数,以便找到第一个容器元素,该元素对于mod 2(奇数)的求值不为0。 如果您想将特定值绑定到所讨论的函数对象的第一个参数,bind1st实用程序将以类似的方式工作。

When an element is found to be odd, it is removed with the stl::list::remove member function of our container.  Once no more odd numbers are found we break out of the loop.  After exiting the loop the contents of the list are displayed, and as expected, all remaining values are even.  We then use replace_if with bind2nd again in order do replace all numbers divisible by 4 with the value 99.  This is pretty simple, but powerful as you can see!

如果发现一个元素是奇数,则使用容器的stl :: list :: remove成员函数将其删除。 一旦找不到更多的奇数,我们就跳出循环。 退出循环后,将显示列表的内容,并且按预期方式,所有剩余值都是偶数。 然后,我们再次将bind_if与bind2nd一起使用,以将所有可被4整除的数字替换为值99。这非常简单,但是功能强大!

Example #6 – STL Generate_n and Reverse Algorithms

The last example of the article will demonstrate the generate_n and reverse algorithms of the STL. The generate algorithm comes first as we can use it to make our test data.  We’ll get straight to the code since it is pretty straight-forward!

本文的最后一个示例将演示STL的generate_n反向算法。 生成算法排在第一位,因为我们可以使用它来生成测试数据。 我们将直接看一下代码,因为它很简单!

#include <iostream>
#include <list>
#include <iterator>
#include <algorithm>

int main()
{
    std::cout<<"Using generate to populate list with 10 random numbers.";
    std::list<int> intList;
    std::generate_n(back_inserter(intList), 10, rand);
    
    std::cout<<std::endl<<std::endl<<"Displaying values held in list before modification:\n";
    std::copy(intList.begin(), intList.end(), std::ostream_iterator<int>(std::cout, " "));
    
    std::cout<<std::endl<<std::endl<<"Sorting list contents to show reverse easier.";
    intList.sort();
    
    std::cout<<std::endl<<std::endl<<"Displaying values held in list after sort:\n";
    std::copy(intList.begin(), intList.end(), std::ostream_iterator<int>(std::cout, " "));
    
    std::cout<<std::endl<<std::endl<<"Calling reverse and displaying list after modification:\n";
    std::reverse(intList.begin(), intList.end());
    std::copy(intList.begin(), intList.end(), std::ostream_iterator<int>(std::cout, " "));
    
    std::cout<<std::endl<<std::endl; //Formatting
}

Our example starts by populating the container with the generate_n algorithm.  As you can see, it is one line and manages to populate our integer list with 10 random numbers using a back_inserter... not bad at all.  The first parameter is the destination, the second is the number of items to generate, and the last is how you wish to generate them.

我们的示例首先使用generate_n算法填充容器。 如您所见,这是一行,并且使用back_inserter设法用10个随机数填充我们的整数列表...一点也不差。 第一个参数是目的地,第二个参数是要生成的项目数,最后一个是您希望如何生成它们。

Next, the contents are outputted and then sorted so that the reverse algorithm effects will be more visible.  The sorted contents are displayed and then the reverse algorithm is called to reverse the contents in the specified range, which is the whole container again.  That’s all there is to it.

接下来,输出内容,然后对其进行排序,以使反向算法效果更加明显。 显示已排序的内容,然后调用反向算法以反转指定范围内的内容,该范围又是整个容器。 这里的所有都是它的。

Conclusion

结论

After reading this article you hopefully have a greater understanding of the basic architecture of the STL and, more specifically, of the purpose of the algorithmic components provided within it.  The examples should have provided you with a basic look at the various categories of functions provided (population, search, manipulation, and functional application) by STL algorithms for use on STL containers.  You also should now be aware of function objects and predicate functions and of how they must be used as parameters in STL algorithms to get the full range of use out of them.  

阅读本文后,您希望对STL的基本体系结构有更深入的了解,更具体地说,对其中提供的算法组件的用途有了更深入的了解。 这些示例应该使您基本了解了STL算法在STL容器上使用的各种功能类别(填充,搜索,操纵和功能应用程序)。 现在,您还应该了解函数对象和谓词函数,以及必须如何将它们用作STL算法中的参数才能充分利用它们。

One last piece of knowledge that I feel should be known is that STL algorithms are capable of processing any sequence that provides an iterator, and pointers are in fact iterators (while iterators are not necessarily pointers).  So, you can happily use something like an array in an STL algorithm by inserting pointers as its iterators (though you'll have to find the position for the end pointer which should be one spot after the last element by yourself if you're processing that far!).

我认为应该知道的最后一项知识是STL算法能够处理提供迭代器的任何序列,而指针实际上是迭代器(而迭代器不一定是指针)。 因此,您可以通过插入指针作为迭代器来愉快地在STL算法中使用类似数组的内容(尽管如果要处理,您必须自己找到末尾指针的位置,该位置应该在最后一个元素之后一个位置)那么远!)。

Well, that's it.  I hope you enjoyed reading this article and learned something useful while doing so.  If you did and have any interest in POSIX threading, check out my previous article here:

好,就是这样。 我希望您喜欢阅读本文,并在此过程中学到了一些有用的东西。 如果您有兴趣并且对POSIX线程感兴趣,请在这里查看我的上一篇文章:

POSIX Threads Programming in C++ C ++中的POSIX线程编程

Please feel free to leave comments and suggestions on the article--I’ll try to address them quickly--and if you liked the article, click “yes” on the useful links above to show your support!

请随时在文章上留下评论和建议-我会尽快解决它们-如果您喜欢该文章,请在上面的有用链接上单击“是”以表示支持!

References

参考资料

This article was primarily written using common knowledge, but STL algorithms were also researched using "The C++ Standard Library: A Tutorial and Reference" by Nicolai M. Josuttis.  This is an excellent book, and I highly recommend it.

本文主要是使用常识撰写的,但STL算法也由Nicolai M. Josuttis的“ The C ++ Standard Library:A Tutorial and Reference”进行了研究。 这是一本很棒的书,我强烈推荐它。

[Special thanks to Evilrix for editing this article so thoroughly and helping me learn alot about the STL in the process.]

[特别感谢Evilrix对本文进行了如此详尽的编辑,并帮助我在此过程中学习了很多有关STL的知识。]

Thanks for reading,

谢谢阅读,

-w00te

-w00te

[John Humphreys]

[约翰·汉弗莱斯]

翻译自: https://www.experts-exchange.com/articles/3183/An-Introduction-to-STL-Algorithms.html

stl算法

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值