使用 foreach 语法迭代时如何检查我是否在最后一个元素上

How can I check if I'm on the last element when iterating using foreach syntax

本文关键字:是否 最后一个 元素 检查 迭代 语法 foreach 何检查 使用      更新时间:2023-10-16

例如:

for( auto &iter: item_vector ) {
     if(not_on_the_last_element) printf(", ");
}

for( auto &iter: skill_level_map ) {
     if(not_on_the_last_element) printf(", ");
}

你真的不能。这就是范围的意义所在,你不需要迭代器。但是,如果不是第一个,您可以更改有关如何打印逗号的逻辑来打印它:

bool first = true;
for (auto& elem : item_vector) {
    if (!first) printf(", ");
    // print elem
    first = false;
}

如果这是循环的意图。或者您可以比较地址:

for (auto& elem : item_vector) {
    if (&elem != &item_vector.back()) printf(", ");
    // ...
}

没有很好的方法。但是,如果我们可以轻松访问容器的最后一个元素......

std::vector<int> item_vector = ...;
for (auto & elem : item_vector) {
    ...
    if (&elem != &item_vector.back())
        printf(", ");
}

这些类型的循环最好使用">循环半"结构编写:

#include <iostream>
#include <vector>
int main()
{
    auto somelist = std::vector<int>{1,2,3,4,5,6,6,7,8,9,6};
    auto first = begin(somelist), last = end(somelist);
    if (first != last) {                // initial check
        while (true) {
            std::cout << *first++;     
            if (first == last) break;   // check in the middle
            std::cout << ", ";
        }
    }
}

打印的实时示例

1, 2, 3, 4, 5, 6, 6

, 7, 8, 9, 6

即在最后一个元素的末尾没有分隔符。

中间的检查使它与 do-while (预先检查(或for_each/基于范围(在最后检查(不同。尝试在这些循环上强制常规 for 循环将引入额外的条件分支或重复的程序逻辑。

就像一个状态模式。

#include <iostream>
#include <vector>
#include <functional>
int main() {
    std::vector<int> example = {1,2,3,4,5};
    typedef std::function<void(void)> Call;
    Call f = [](){};
    Call printComma = [](){ std::cout << ", "; };
    Call noPrint = [&](){ f=printComma; };
    f = noPrint;
    for(const auto& e:example){
        f();
        std::cout << e;
    }
    return 0;
}

Output:
1, 2, 3, 4, 5

第一次通过f指向noPrint,这只能使f然后指向printComma,因此逗号仅在第二个和后续项目之前打印。

将此

代码安全地存储在小实用程序袋中的头文件中:

namespace detail {
    template<class Iter>
    struct sequence_emitter
    {
        sequence_emitter(Iter first, Iter last, std::string sep)
        : _first(std::move(first))
        , _last(std::move(last))
        , _sep(std::move(sep))
        {}
        void write(std::ostream& os) const {
            bool first_element = true;
            for (auto current = _first ; current != _last ; ++current, first_element = false)
            {
                if (!first_element)
                    os << _sep;
                os << *current;
            }
        }
    private:
        Iter _first, _last;
        std::string _sep;
    };
    template<class Iter>
    std::ostream& operator<<(std::ostream& os, const sequence_emitter<Iter>& se) {
        se.write(os);
        return os;
    }
}
template<class Iter>
detail::sequence_emitter<Iter>
emit_sequence(Iter first, Iter last, std::string separator = ", ")
{
    return detail::sequence_emitter<Iter>(std::move(first), std::move(last), std::move(separator));
}

然后,您可以发出任何容器的任何范围,而无需尾随分隔符,如下所示:

vector<int> x { 0, 1, 2, 3, 4, 5 };
cout << emit_sequence(begin(x), end(x)) << endl;
set<string> s { "foo", "bar", "baz" };
cout << emit_sequence(begin(s), end(s), " comes before ") << endl;

预期输出:

0, 1, 2, 3, 4, 5
bar comes before baz comes before foo

基于范围的 for 循环用于迭代整个范围。如果你不想这样,你为什么不做一个常规的 for 循环呢?

auto end = vector.end() - 1;
for (auto iter = vector.begin(); iter != end; ++iter) {
   // do your thing
   printf(", ");
}
// do your thing for the last element

如果您不想像我一样重复两次代码来"做你的事",那么创建一个 lambda 来调用它:

auto end = vector.end() - 1;
// create lambda
for (auto iter = vector.begin(); iter != end; ++iter) {
   lambda(*iter);
   printf(", ");
}
lambda(vector.back());
相关文章: