Разрыв в цикле std :: for_each - PullRequest
       9

Разрыв в цикле std :: for_each

26 голосов
/ 17 апреля 2009

При использовании алгоритма std :: for_each, как прервать выполнение определенного условия?

Ответы [ 7 ]

21 голосов
/ 13 июля 2012

Вы можете использовать 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
  });

Однако это расточительное решение (возвращаемые значения на самом деле ни для чего не используются), и вам лучше написать собственный цикл.

14 голосов
/ 17 апреля 2009

Вы можете выйти из for_each (), выбрасывая исключение из вашего функтора. Однако это часто не очень хорошая идея, и есть альтернативы.

Вы можете сохранить состояние в вашем функторе. Если вы обнаружите условие 'break', просто установите флаг в вашем функторе, а затем для каждой последующей итерации просто возвращайтесь, не выполняя действия вашего функтора. Очевидно, что это не остановит итерацию, которая может быть дорогой для больших коллекций, но по крайней мере остановит выполнение работы.

Если ваша коллекция отсортирована, вы можете найти () элемент, на который хотите разбить, а затем выполните for_each от begin () до возвращаемого элемента find ().

Наконец, вы можете реализовать for_each_if(). Это снова не остановит итерацию, но не оценит ваш функтор, который выполняет работу, если предикат оценивается как ложный. Вот 2 варианта for_each_xxx(), один из которых принимает значение и выполняет работу, если оператор == () имеет значение true, а другой - два функтора; один, который выполняет сравнение ala 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;
    };
13 голосов
/ 17 апреля 2009

Вы можете использовать алгоритм find_if, который остановит и вернет итератор, где условие предиката, примененное к итерированному элементу, возвращает true. Таким образом, ваш предикат должен быть изменен, чтобы он возвращал логическое значение как условие продолжения / прерывания.

Однако, это взлом, поэтому вы можете использовать алгоритмы.

Другой способ - использовать BOOST_FOREACH.

6 голосов
/ 17 апреля 2009

Если вы хотите выполнить некоторые действия, когда условие не выполнено, возможно, вам нужно изменить алгоритм на что-то вроде std::find_if?

4 голосов
/ 17 апреля 2009

Как уже показали другие, это возможно только с помощью обходных путей, которые ИМХО запутывают код.

Поэтому я предлагаю изменить for_each на обычный цикл for. Это сделает более видимым для других, что вы используете break (и, возможно, даже продолжаете).

2 голосов
/ 17 апреля 2009

Вы не можете сделать это, если не выбросите исключение, что не очень хорошая идея, потому что вы не выполняете управление потоком с исключениями.

Обновление: очевидно, Boost имеет for_each_if, который может помочь, но вы не используете Boost.

0 голосов
/ 17 апреля 2009

Вы бросаете исключение. Является ли это хорошей идеей, это своего рода вопрос стиля, темп @Dan, но может быть больше проблемы с вашим дизайном. for_each предназначен для своего рода стиля функционального программирования, который неявно предполагает, что ваша функция может применяться равномерно по всему набору. Так что, если вам нужно нужно сломать, это может считаться необычным условием и, следовательно, достойным исключения.

Другое и более «функциональное» решение состоит в том, чтобы написать свою функцию так, чтобы, если она не влияла на некоторые приложения, напишите ее, чтобы она не имела никакого эффекта. Так, например, если у вас была функция суммирования, добавьте 0 в случаях, из которых вы бы «сломались».

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...