Best way to iterate through a for loop, and then iterate through it again backwards?

StackOverflow https://stackoverflow.com/questions/23480824

  •  16-07-2023
  •  | 
  •  

سؤال

I found that when writing animations I sometimes run into having to go through a for loop once, then iterate the value down afterwards. This was generally used for jump animations, or disappear then appear again animations.

Here's an example of what I had done -

// Make the sprite slowly disappear
for (int i = 256; i > 0; --i)
{
    sprite.opacity(i);
    draw();
}

// Make the sprite slowly appear again
for (int i = 0; i < 256; ++i)
{
    sprite.opacity(i);
    draw();
}

Every time I did this I had a deep feeling that it was too much. What would be a nicer way of going about this? I'm not entirely sure what would be best practice. I imagine I could use reverse_iterator, but I'm also not sure how I would implement it.

هل كانت مفيدة؟

المحلول

Consider the use of <cmath> abs() function:

for( int i = -255; i <= 255; i++)
    use( abs( i ) );

نصائح أخرى

You can use the absolute value function abs() defined in <cmath>. It will halve the code written in your case.

for(int i=0; i<512; ++i)
{
    sprite.opacity( abs(256-i) );
    draw();
}

I believe in the situation you are describing, you have to iterate through the sprites to set the opacity of each sprite. Whether you use a for loop, or a reverse_iterator, the time spent is going to be the same. Any implementation of the reverse_iterator will still have to iterate through each sprite. There might be ways to make it easier to read, but in the end the algorithm will come down to the same. For example, you could take advantage of the stack and call the sprites recursively to increase the opacity and then decrease on the way back out; however, I see no gain in doing so the algorithm time would still end up being the same.

In some cases, you just need to bite the bullet and spend the time doing things in a way that may seem like (or even be) brute force.

That's a great way to iterate through a loop both forward and "in reverse" - one commonly used by C++ programmers.

For your sprite, it appears that the 256 range (you might consider setting a const int RGB_RANGE equal to 256 - or a more appropriate identifier) is all that is needed; however, were the size of your object dynamic, you could also consider using the .size() function (something like an ArrayList or a vector - here is where something like an iterator would be useful):

for (i = 9; i < RGB_RANGE; i++)
{
    // CODE
}

The above code being an example of the first const suggestion. Remember, simple code is never a bad thing - it means you are doing something right.

If you don't want to use abs, I'd go with something like :

template<typename Func>
void animate (size_t step_count, Func && f)
{
    size_t step;

    for (step = step_count ; step > 0 ; --step)
        f(step - 1);

    for (step = 1 ; step < step_count ; ++step)
        f(step);
}

Use case :

animate(256, [](size_t step)
{
    sprite.opacity(step);
    draw();
});

If you wish to just iterate a range up and down again, you can go the very crazy route and just define a "container" (or range, in boost lingo) that provides iterators (well, technically they are more almost-iterators) which allow you to express exactly what you intend to do:

for(auto i : down_and_up(3)) ::std::cout << i << "\n";

For example should print

3
2
1
0
1
2

Sadly, there is not much support in the standard library for types like this, although boost provides boost::iterator_range, boost::counting_iterator, and boost::join that, in concert with std::reverse_iterator, can provide down_and_up. Writing one yourself if fairly simple (although verbose), as long as you do not completely abuse it:

struct down_and_up
{
    size_t from;
    down_and_up(size_t const from) : from(from) { }
    struct iterator : public ::std::iterator<::std::forward_iterator_tag, size_t> {
        size_t cur;
        bool down;

        iterator(size_t cur, bool down) : cur(cur), down(down) { }

        size_t operator*() const { return cur; }
        iterator& operator++()
        {
            if(down)
            {
                --cur;
                if(0 == cur) down = false;
            }
            else ++cur;
            return *this;
        }
        friend bool operator==(iterator const& lhs, iterator const& rhs) { return lhs.down == rhs.down && lhs.cur == rhs.cur; }
        friend bool operator!=(iterator const& lhs, iterator const& rhs) { return lhs.down != rhs.down || lhs.cur != rhs.cur; }
    };


    iterator begin() const { return iterator{ from, true }; }
    iterator end() const { return iterator{ from, false }; }
};

Note: If you wish, you can easily extend it with more container capabilities, like a value_type member typedef, but this definition is enough for the above example.

P.S.: The boost way, for your entertainment:

boost::iterator_range<boost::counting_iterator<size_t>> up(boost::counting_iterator<size_t>(0), boost::counting_iterator<size_t>(3));
boost::iterator_range<std::reverse_iterator<boost::counting_iterator<size_t>>> down(
    std::reverse_iterator<boost::counting_iterator<size_t>>(boost::counting_iterator<size_t>(4)),
    std::reverse_iterator<boost::counting_iterator<size_t>>(boost::counting_iterator<size_t>(1)));

for(auto i : boost::join(down, up)) ::std::cout << i << "\n";
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top