打破 std::for_each 循环

问题描述 投票:0回答:9

使用 std::for_each 算法时,满足特定条件时如何中断?

c++ stl
9个回答
33
投票

您可以使用 std::any_of (或 std::all_of 或 std::none_of),例如像这样:

std::vector<int> a;
// ...     
std::all_of(a.begin(), a.end(), [&](int val) { 
  // return false if you want to break, true otherwise
  });

但是,这是一个浪费的解决方案(返回值并没有真正用于任何用途),你最好编写自己的循环。


18
投票

您可以使用

std::find_if
算法,该算法将停止并将迭代器返回到应用谓词条件返回
true
的第一个元素。因此,您的谓词应该更改为返回布尔值作为继续/中断条件。

但是,这是一个 hack,所以你可以使用这些算法。

另一种方法是使用 BOOST_FOREACH。


16
投票

您可以通过从函子抛出异常来中断 for_each()。然而,这通常不是一个好主意,还有其他选择。

您可以在函子中保留状态。如果您检测到“中断”条件,只需在函子中设置一个标志,然后对于每个后续迭代,只需返回而不执行函子的操作。显然,这不会停止迭代,这对于大型集合来说可能会很昂贵,但它至少会停止执行工作。

如果您的集合已排序,您可以 find() 想要中断的元素,然后从 begin() 执行 for_each 到 find() 返回的元素。

最后,你可以实现一个

for_each_if()
。这将再次不会停止迭代,但不会评估函子,如果谓词计算结果为 false,则函子将完成工作。这里有 2 种类型的
for_each_xxx()
,一种是接受一个值并在 operator==() 计算结果为 true 时执行工作,另一种接受两个函子;一个执行 find_if() 比较,另一个执行比较运算符计算结果为 true 的工作。

/* ---

    For each
    25.1.1

        template< class InputIterator, class Function, class T>
            Function for_each_equal(InputIterator first, InputIterator last, const T& value, Function f)

        template< class InputIterator, class Function, class Predicate >
            Function for_each_if(InputIterator first, InputIterator last, Predicate pred, Function f)

    Requires:   

        T is of type EqualityComparable (20.1.1) 

    Effects:    

         Applies f to each dereferenced iterator i in the range [first, last) where one of the following conditions hold:

            1:  *i == value
            2:  pred(*i) != false

    Returns:    

        f

    Complexity: 

        At most last - first applications of f

    --- */

    template< class InputIterator, class Function, class Predicate >
    Function for_each_if(InputIterator first, 
                         InputIterator last, 
                         Predicate pred, 
                         Function f)
    {
        for( ; first != last; ++first)
        {
            if( pred(*first) )
                f(*first);
        }
        return f;
    };

    template< class InputIterator, class Function, class T>
    Function for_each_equal(InputIterator first, 
                            InputIterator last, 
                            const T& value, 
                            Function f)
    {
        for( ; first != last; ++first)
        {
            if( *first == value )
                f(*first);
        }
        return f;
    };

8
投票

如果您想在条件不满足时执行某些操作,也许您需要更改算法,例如

std::find_if


7
投票

正如其他人已经表明的那样,只有通过恕我直言混淆代码的解决方法才能实现。

所以我的建议是将 for_each 更改为常规 for 循环。这将使其他人更容易看到您正在使用中断(甚至可能继续)。


3
投票

你不能这样做,除非你抛出异常,这不是一个好主意,因为你不使用异常进行流量控制。

更新:显然 Boost 有一个 for_each_if 可能有帮助,但你没有使用 Boost。


1
投票

你抛出了一个异常。这是否是一个好主意是一种风格问题,节奏@Dan,但可能更多的是你的设计的问题。 for_each 旨在用于一种函数式编程风格,它隐式地假设您的函数可以在整个集合中统一应用。因此,如果您确实需要打破,这可能被视为不寻常的情况,因此值得例外。

另一个解决方案,也是一个更“功能化”的解决方案,是编写您的函数,以便如果它不应该对某些应用程序产生影响,则将其编写为没有影响。因此,举例来说,如果您有一个求和函数,请让它在您“中断”的情况下添加 0。


0
投票

您可以使用

std::find_if
代替
std::for_each
:

int aaa[]{ 1, 2, 3, 4, 5, 6, 7, 8 };
std::find_if(aaa, std::next(aaa, sizeof(aaa) / sizeof(int)), [](const auto &i) {
    if (i == 5)
        return true;
    std::cout << i << std::endl;
    return false;
});

输出:
1
2
3
4


0
投票

承认这是糟糕且不完整的代码,并且这里的所有其他建议都更好,我想证明至少可以通过包装容器并定义新的迭代器来完成OP所要求的操作。孩子们,请不要在家里这样做: 神箭

#include <iostream>
#include <vector>
#include <algorithm>
#include <optional>

using namespace std;
template<typename C>
struct ContainerRef{
    using orig_const_iterator = typename C::const_iterator;
    struct const_iterator{
        struct Internal{
            orig_const_iterator iter;
            orig_const_iterator last;
            bool stop;
        };
        const_iterator* ptr;
        std::optional<Internal> state;
 
        const_iterator(orig_const_iterator iter, orig_const_iterator last ):
            state(Internal{
                .iter = iter,
                .last = last,
                .stop = false
            }) {
            ptr = 0;
        }

        const_iterator(const_iterator& other):ptr(&other){}

        bool get_stop() const{
            return ptr? ptr->state->stop:false;
        }

        const_iterator& operator ++(int){
            if (ptr) (*ptr).state->iter++;
            else state->iter++;
            return &this;
        }

        const_iterator& operator++(){
            if (ptr) ++((*ptr).state->iter);
            else state->iter;
            return *this;
        }

        orig_const_iterator& get_last(){
            return (ptr != nullptr)? ptr->last : this->last;
        }

        bool operator!=(const_iterator& i) const{
            return (ptr? (*ptr).operator!=(i) : state->last != state->iter) && !get_stop();
        }

        const typename C::value_type& operator*(){
            return *(ptr? ptr->state->iter: state->iter) ;
        }

        void exit() {
            if (ptr) ptr->exit();
            else state->stop = true;
        }
    };

    const_iterator current;
    const_iterator last;
 
    const_iterator& begin(){
        return current;
    }

    const_iterator&  end(){
        return last;
    }

    void exit(){
        current.exit();
    }

    ContainerRef(const C& c): current(c.begin(), c.end()),last(c.begin(),c.end()){}
};

int main(){
    std::vector numbers {1,2,3,4,5,6,7,8,9};
    ContainerRef ref(numbers);
    for_each(ref.begin(),ref.end(),[&ref](int i){
        if (i>4){
            ref.exit();

        }
        else{
            cout<<i<< (i==4? "":", ");
        }
    });
    cout<<"\n";
}
© www.soinside.com 2019 - 2024. All rights reserved.