使用 std::for_each 算法时,满足特定条件时如何中断?
您可以使用 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
});
但是,这是一个浪费的解决方案(返回值并没有真正用于任何用途),你最好编写自己的循环。
std::find_if
算法,该算法将停止并将迭代器返回到应用谓词条件返回 true
的第一个元素。因此,您的谓词应该更改为返回布尔值作为继续/中断条件。
但是,这是一个 hack,所以你可以使用这些算法。
另一种方法是使用 BOOST_FOREACH。
您可以通过从函子抛出异常来中断 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;
};
如果您想在条件不满足时执行某些操作,也许您需要更改算法,例如
std::find_if
?
正如其他人已经表明的那样,只有通过恕我直言混淆代码的解决方法才能实现。
所以我的建议是将 for_each 更改为常规 for 循环。这将使其他人更容易看到您正在使用中断(甚至可能继续)。
你不能这样做,除非你抛出异常,这不是一个好主意,因为你不使用异常进行流量控制。
更新:显然 Boost 有一个 for_each_if 可能有帮助,但你没有使用 Boost。
你抛出了一个异常。这是否是一个好主意是一种风格问题,节奏@Dan,但可能更多的是你的设计的问题。 for_each 旨在用于一种函数式编程风格,它隐式地假设您的函数可以在整个集合中统一应用。因此,如果您确实需要打破,这可能被视为不寻常的情况,因此值得例外。
另一个解决方案,也是一个更“功能化”的解决方案,是编写您的函数,以便如果它不应该对某些应用程序产生影响,则将其编写为没有影响。因此,举例来说,如果您有一个求和函数,请让它在您“中断”的情况下添加 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
承认这是糟糕且不完整的代码,并且这里的所有其他建议都更好,我想证明至少可以通过包装容器并定义新的迭代器来完成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";
}