How can I avoid “for” loops with an “if” condition inside them with C++?

前端 未结 13 1692
滥情空心
滥情空心 2021-01-30 03:31

With almost all code I write, I am often dealing with set reduction problems on collections that ultimately end up with naive \"if\" conditions inside of them. Here\'s a simple

相关标签:
13条回答
  • 2021-01-30 04:13

    Also, if you don't care reordering the collection, std::partition is cheap.

    #include <iostream>
    #include <vector>
    #include <algorithm>
    #include <functional>
    
    void DoStuff(int i)
    {
        std::cout << i << '\n';
    }
    
    int main()
    {
        using namespace std::placeholders;
    
        std::vector<int> v {1, 2, 5, 0, 9, 5, 5};
        const int SOMETHING = 5;
    
        std::for_each(v.begin(),
                      std::partition(v.begin(), v.end(),
                                     std::bind(std::equal_to<int> {}, _1, SOMETHING)), // some condition
                      DoStuff); // action
    }
    
    0 讨论(0)
  • 2021-01-30 04:14

    One style that gets used enough to mention, but hasn't been mentioned yet, is:

    for(int i=0; i<myCollection.size(); i++) {
      if (myCollection[i] != SOMETHING)
        continue;
    
      DoStuff();
    }
    

    Advantages:

    • Doesn't change the indentation level of DoStuff(); when condition complexity increases. Logically, DoStuff(); should be at the top-level of the for loop, and it is.
    • Immediately makes it clear that the loop iterates over the SOMETHINGs of the collection, without requiring the reader to verify that there is nothing after the closing } of the if block.
    • Doesn't require any libraries or helper macros or functions.

    Disadvantages:

    • continue, like other flow control statements, gets misused in ways that lead to hard-to-follow code so much that some people are opposed to any use of them: there is a valid style of coding that some follow that avoids continue, that avoids break other than in a switch, that avoids return other than at the end of a function.
    0 讨论(0)
  • 2021-01-30 04:23

    I'll just mention Mike Acton, he would definitely say:

    If you have to do that, you have a problem with your data. Sort your data!

    0 讨论(0)
  • 2021-01-30 04:25

    Another solution in case the i:s are important. This one builds a list that fills in the indexes of which to call doStuff() for. Once again the main point is to avoid the branching and trade it for pipelineable arithmetic costs.

    int buffer[someSafeSize];
    int cnt = 0; // counter to keep track where we are in list.
    for( int i = 0; i < container.size(); i++ ){
       int lDecision = (container[i] == SOMETHING);
       buffer[cnt] = lDecision*i + (1-lDecision)*buffer[cnt];
       cnt += lDecision;
    }
    
    for( int i=0; i<cnt; i++ )
       doStuff(buffer[i]); // now we could pass the index or a pointer as an argument.
    

    The "magical" line is the buffer loading line that arithmetically calculates wether to keep the value and stay in position or to count up position and add value. So we trade away a potential branch for some logics and arithmetics and maybe some cache hits. A typical scenario when this would be useful is if doStuff() does a small amount of pipelineable calculations and any branch in between calls could interrupt those pipelines.

    Then just loop over the buffer and run doStuff() until we reach cnt. This time we will have the current i stored in the buffer so we can use it in the call to doStuff() if we would need to.

    0 讨论(0)
  • 2021-01-30 04:27

    IMHO it's more straight forward and more readable to use a for loop with an if inside it. However, if this is annoying for you, you could use a for_each_if like the one below:

    template<typename Iter, typename Pred, typename Op> 
    void for_each_if(Iter first, Iter last, Pred p, Op op) {
      while(first != last) {
        if (p(*first)) op(*first);
        ++first;
      }
    }
    

    Usecase:

    std::vector<int> v {10, 2, 10, 3};
    for_each_if(v.begin(), v.end(), [](int i){ return i > 5; }, [](int &i){ ++i; });
    

    Live Demo

    0 讨论(0)
  • 2021-01-30 04:27
    for(auto const &x: myCollection) if(x == something) doStuff();
    

    Looks pretty much like a C++-specific for comprehension to me. To you?

    0 讨论(0)
提交回复
热议问题