如何从std :: vector或list中选择一个子集?

时间:2013-11-23 22:07:03

标签: c++ boost stl

c ++大师:

有一些有用的c ++ stl算法,例如find或search。但是,似乎他们只返回一个单一的交互者。

如果我想为STL容器执行SQL样式'select'怎么办?比方说,一个矢量(可能会扩展到列表或地图)。

之类的东西
std::pair<vector::iterator, vector::iterator> select(std::vector::iterator begin, std::vector::iterator end, Comparor equal_to)

输出应该是一个范围,类似于std :: pair,类似于boost :: multi-index

中方法的返回值

在stl中有这样的东西吗?或者类似的任何可靠的图书馆?

4 个答案:

答案 0 :(得分:5)

你基本上有两种方法:

1)你在上面的评论中说的东西,将结果写入(迭代器指向)迭代器的容器。这看起来像这样:

template <typename ForwardIterator, typename OutputIterator, typename UnaryPredicate>
void select_iterators(ForwardIterator first, ForwardIterator last, 
                      OutputIterator out, UnaryPredicate pred) {
    while (first != last) {
        if pred(*first) *out++ = first;
        ++first;
    }
}

然后你称之为:

vector<Foo> myfoos;
vector<vector<Foo>::iterator> results;
select_iterators(myfoos.begin(), myfoos.end(), std::back_inserter(results), some_comparator);

您可以使用select_iteratorscopy_if在其他算法方面实际定义boost::counting_iterator,但是当直接实现如此简单时,我认为这不值得。它看起来像是:

template <typename ForwardIterator, typename OutputIterator, typename UnaryPredicate>
void select_iterators(ForwardIterator first, ForwardIterator last, 
                      OutputIterator out, UnaryPredicate pred) {
    std::copy_if(
        boost::make_counting_iterator(first), 
        boost::make_counting_iterator(last),
        out,
        [&](ForwardIterator it) { return pred(*it); }
    );
}

2)不是预先测试所有值并在某处写入结果,而是定义一个迭代器,每次递增时它会超过原始范围,直到找到下一个匹配。 Boost提供了两种方法:boost::filter_iteratorboost::adaptors::filter。所以你可以写:

auto results = boost::adaptors::filter(myfoos, some_comparator);

然后,无论您想对结果做什么,都可以从results.begin()迭代到results.end(),就好像它是一个容器一样。它不是容器,它不满足整个容器界面。它满足由Boost定义的接口,名为Range,相当于“可以迭代”。它实际上只是myfoos的过滤视图,因此不会复制或移动任何Foo对象。

答案 1 :(得分:4)

如果你可以修改你的向量std::partition将是你的选择。在这里你怎么称呼它:

std::vector<int>::iterator p =
    std::partition(v.begin(), v.end(), you_predicate);

您在v.begin()p之间回答谎言。

答案 2 :(得分:2)

您可能正在寻找boost::range

boost::range实际上是一对迭代器,用于分隔容器的一系列元素。该库包括各种算法,这些算法返回范围内的范围(例如容器中等效值的范围,以及用户提供的等价仿函数)。

答案 3 :(得分:1)

template<typename ForwardIterator, typename OutputIterator, typename UnaryPredicate>
    void find_elements(ForwardIterator first, ForwardIterator last, OutputIterator out, UnaryPredicate pred)
    {
        while(first != last)
        {
            if(pred(*first))
                *out++ = first;
            ++first;
        }
    }

要记住的事情:

1。)您说您希望您的容器为iterator而不是const_iterator。类型将与传递给函数的开始和结束范围相同。例如,const_iterator容器的类型为const,如果您使用const_iteratorvector::cbegin,则类型也为vector::cend,并且无法编译如果您使用不同的迭代器,例如vector::beginvector::cend

2。)向量经常失去迭代器有效性,因此使用这些迭代器时要小心。例如,如果添加到向量,则此函数返回的每个迭代器都可能无效。要防止这种情况发生,请使用其他容器(例如列表)或使用vector::reserve

3.)前向迭代器必须是支持++的东西,并且在解除引用时,与InputIterator具有相同的类型(例如vector<int>::iterator)。在递增它之后它也必须保持有效的迭代器,否则函数将毫无意义。输出迭代器必须到达一个有足够空间的位置来容纳所有找到satsify pred的迭代器。如果您事先不知道空格,可以使用std::back_inserter中的<iterator>与已定义container::push_back的容器,并且会根据需要增加。

以下是对功能的测试,以便您了解其工作原理。

int main()
{
    vector<string> ss = {"hi", "yog", "engils", "pog"};

    // Define a predicate
    auto isSizeThree = [](string const &s)
    {
        return s.size() == 3;
    };

    // example one: Somehow I know how many satisfy the predicate. I just don't know where they are.
    vector<vector<string>::iterator> answer(2);
    find_elements(begin(ss), end(ss), begin(answer), isSizeThree);

    // Check answer
    cout << "Test 1" << endl;
    for(auto entry : answer)
        cout << *entry << endl;

    // Example two: I don't know how many there will be (more typical). If I use a continer with stuff in it - it will stack right on the back of it!
    find_elements(begin(ss), end(ss), back_inserter(answer), isSizeThree);

    // Check answer (has the answer from test 1 still in it)
    cout << "Test 2" << endl;
    for(auto entry : answer)
        cout << *entry << endl;

    // Example Three: Same as test 2 but clear the ansewr vector first.
    answer.clear();
    find_elements(begin(ss), end(ss), back_inserter(answer), isSizeThree);

    // Check answer
    cout << "Test 3" << endl;
    for(auto entry : answer)
        cout << *entry << endl;
}