遍历一个for循环的最佳方法,然后倒序再次遍历它是什么?

3
我发现在编写动画时,有时需要先通过for循环遍历一次,然后再递减迭代值。这通常用于跳跃动画或消失再出现的动画。
以下是我所做的示例 -
// 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();
}

每次我这么做都觉得有点过了。有没有更好的方法?我不是很确定最佳实践是什么。我想我可以使用reverse_iterator,但我也不知道如何实现它。


你说得很对,这确实是“太多”的问题:这是C++的一个常见问题。在C++11中,一些循环可以写成基于范围的for循环,否则你就几乎没有办法了。 - IdeaHat
我认为你的方法没有任何问题。 - sergico
如果你想避免重复代码,也许使用lambda表达式是个好主意。 - PlasmaHH
第一个循环中使用的i值范围是错误的。您应该使用for (int i = 255; i >= 0; i--) - R Sahu
6个回答

12

考虑使用<cmath> 的abs()函数:

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

+1 比我写的好。我想知道如何引入减法来实现梯形函数,就像我做的和 @varaquilex 做的一样。嗯。 - Cheers and hth. - Alf
3
与没有使用abs的两个循环相比,这个的成本如何? - Chnossos
只需一份代码来处理、扩展和纠正,而不是两份——无价之宝! :) - CiaPan
这是聪明的 CiaPan,我非常喜欢它!非常感谢。 - user3419168

2
您可以使用在<cmath>中定义的绝对值函数abs()。这将使您编写的代码减少一半。
for(int i=0; i<512; ++i)
{
    sprite.opacity( abs(256-i) );
    draw();
}

1
如果您不想使用 abs,我建议使用类似以下内容的代码:
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);
}

使用案例:
animate(256, [](size_t step)
{
    sprite.opacity(step);
    draw();
});

1
如果你只想迭代一个范围并再次循环,你可以走非常疯狂的路线,只需定义一个“容器”(或范围,在boost术语中),提供迭代器(技术上它们更像是几乎迭代器),这样你就可以表达出你想做的事情。
for(auto i : down_and_up(3)) ::std::cout << i << "\n";

例如应该打印
3
2
1
0
1
2

遗憾的是,标准库中对于这种类型的支持不多,但boost提供了boost::iterator_rangeboost::counting_iteratorboost::join,与std::reverse_iterator共同使用,可以提供down_and_up。如果你不完全滥用它,编写一个自己的迭代器相当简单(尽管冗长):

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

注意:如果您愿意,可以轻松地扩展它的容器功能,比如一个value_type成员类型定义,但是上述定义已经足够了。
附注:为您的娱乐,这是boost的方式:
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";

1
这是一种循环迭代的好方法,可以向前和“向后”遍历 - 这是C++程序员通常使用的方法。
对于您的精灵,似乎只需要256范围(您可以考虑将 const int RGB_RANGE 设置为256-或更合适的标识符); 但是,如果对象的大小是动态的,您还可以考虑使用 .size()函数(类似于 ArrayList vector - 这是迭代器非常有用的地方)。
for (i = 9; i < RGB_RANGE; i++)
{
    // CODE
}

上面的代码是第一个const建议的示例。记住,简单的代码从来不是坏事 - 这意味着你正在做正确的事情。

1
我认为在你所描述的情况下,你需要遍历精灵以设置每个精灵的不透明度。无论你使用for循环还是reverse_iterator,花费的时间都将是相同的。任何reverse_iterator的实现都仍然需要遍历每个精灵。可能有使其更易于阅读的方法,但最终算法将归结为相同的结果。例如,你可以利用堆栈并递归调用精灵来增加不透明度,然后在回退时减少;然而,我看不到这样做的好处,因为算法时间仍将相同。
在某些情况下,你只需要咬紧牙关,并花时间以看似(甚至是)蛮力的方式完成任务。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接