web-dev-qa-db-ja.com

std :: for_eachループの中断

Std :: for_eachアルゴリズムを使用しているときに、特定の条件が満たされたときにどのように中断しますか?

26
Naveen

Find_ifアルゴリズムを使用できます。これは、反復要素に適用された述語条件がtrueを返すイテレーターを停止して返します。したがって、述語を変更して、継続/中断条件としてブール値を返す必要があります。

ただし、これはハックであるため、アルゴリズムを使用できます。

もう1つの方法は、BOOST_FOREACHを使用することです。

13

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
  });

ただし、これは無駄な解決策であり(戻り値は実際には何にも使用されません)、独自のループを作成することをお勧めします。

21
Anton Belov

ファンクターから例外をスローすることで、for_each()から抜け出すことができます。ただし、これは多くの場合良い考えではなく、代替手段があります。

ファンクターで状態を保持できます。 'break'状態を検出した場合は、ファンクターにフラグを設定するだけで、後続の反復ごとに、ファンクターの処理を行わずに戻るだけです。明らかに、これによって反復が停止することはありません。これは、大規模なコレクションではコストがかかる可能性がありますが、少なくとも作業の実行は停止します。

コレクションがソートされている場合は、ブレークしたい要素をfind()してから、begin()から返された要素find()までfor_eachを実行できます。

最後に、for_each_if()を実装できます。これも反復を停止しませんが、述語がfalseと評価された場合に作業を行うファンクターを評価しません。 for_each_xxx()には2つのフレーバーがあります。1つは値を取り、operator ==()がtrueと評価された場合に作業を実行し、もう1つは2つのファンクターを取ります。 1つはfind_if()で比較を実行し、もう1つは比較演算子が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;
    };
14
John Dibling

条件が満たされていないときにいくつかのアクションを実行したい場合は、std::find_ifのようなアルゴリズムを変更する必要がありますか?

6
bayda

他の人がすでに示しているように、IMHOがコードを難読化するのは回避策によってのみ達成可能です。

したがって、私の提案は、for_eachを通常のforループに変更することです。これにより、breakを使用していることが他の人にわかりやすくなります(場合によっては続行します)。

4
lothar

例外をスローしない限り、それを行うことはできません。例外を使用してフロー制御を行わないため、これはお勧めできません。

更新:明らかにBoostにはfor_each_if that might helpがありますが、Boostを使用していません。

2

例外をスローします。それが良いアイデアであるかどうかは、スタイルの質問のようなもので、@ Danのペースですが、デザインの問題になる可能性があります。 for_eachは、関数がセット全体に均一に適用できることを暗黙的に想定する、一種の関数型プログラミングスタイルを対象としています。したがって、doブレークする必要がある場合、それは異常な状態と見なされる可能性があるため、例外に値します。

もう1つの解決策、およびより「機能的な」解決策は、関数を記述して、一部のアプリケーションに影響がない場合は、影響がないように記述することです。したがって、たとえば、加算関数がある場合、「壊れた」場合に備えて0を加算します。

0
Charlie Martin