5

是否有等效enumerate于 C++ 中 python 的基于范围的循环?我会想象这样的事情。

enumerateLoop (auto counter, auto el, container) {
    charges.at(counter) = el[0];
    aa.at(counter) = el[1];
}

这可以用模板或宏来完成吗?

我知道我可以使用老式的 for 循环并迭代直到达到container.size(). 但我很感兴趣如何使用模板或宏来解决这个问题。

编辑

在评论中的提示之后,我使用了 boost 迭代器。我得到了另一个使用 C++14 的工作解决方案。

template <typename... T>
auto zip(const T &... containers) -> boost::iterator_range<boost::zip_iterator<
decltype(boost::make_tuple(std::begin(containers)...))>> {
  auto zip_begin =
    boost::make_zip_iterator(boost::make_tuple(std::begin(containers)...));
  auto zip_end =
    boost::make_zip_iterator(boost::make_tuple(std::end(containers)...));
  return boost::make_iterator_range(zip_begin, zip_end);
}

template <typename T>
auto enumerate(const T &container) {
return zip(boost::counting_range(0, static_cast<int>(container.size())),
container);
} 

https://gist.github.com/kain88-de/fef962dc1c15437457a8

4

8 回答 8

4

自 C 以来,多个变量的枚举一直是一种习惯用法。唯一的复杂之处是您不能在 for 循环的初始化程序中声明这两个变量。

int index;
for (auto p = container.begin(), index = 0; p != container.end(); ++p, ++index)

我认为没有比这更简单(或更强大)的了。

于 2015-02-27T18:11:14.953 回答
4

对此有一个预 C++11 解决方案:boost.range.indexed。不幸的是,它不适用于基于 C++11 范围的 for 循环,只能用于旧式详细循环。然而,对于 C++17,它应该变得(几乎)和使用结构化绑定的 python 一样简单

然后应该可以实现这样的工作:

for (auto& [n,x] : enumerate(vec)) x = n;

所以,还有一点等待;)

于 2016-09-06T09:55:16.967 回答
3

不久前我为此写了一些东西。

本质上,您需要包装一个迭代器并为其赋予对语义。

AFAIK,语言中没有这样的内容。而且我认为boost也没有。你几乎必须自己动手。

// Wraps a forward-iterator to produce {value, index} pairs, similar to
// python's enumerate()
template <typename Iterator>
struct EnumerateIterator {
private:
  Iterator current;
  Iterator last;
  size_t index;
  bool atEnd;

public:
  typedef decltype(*std::declval<Iterator>()) IteratorValue;
  typedef pair<IteratorValue const&, size_t> value_type;

  EnumerateIterator()
    : index(0), atEnd(true) {}

  EnumerateIterator(Iterator begin, Iterator end)
    : current(begin), last(end), index(0) {
    atEnd = current == last;
  }

  EnumerateIterator begin() const {
    return *this;
  }

  EnumerateIterator end() const {
    return EnumerateIterator();
  }

  EnumerateIterator operator++() {
    if (!atEnd) {
      ++current;
      ++index;

      atEnd = current == last;
    }

    return *this;
  }

  value_type operator*() const {
    return {*current, index};
  }

  bool operator==(EnumerateIterator const& rhs) const {
    return
      (atEnd && rhs.atEnd) ||
      (!atEnd && !rhs.atEnd && current == rhs.current && last == rhs.last);
  }

  bool operator!=(EnumerateIterator const& rhs) const {
    return !(*this == rhs);
  }

  explicit operator bool() const {
    return !atEnd;
  }
};

template<typename Iterable>
EnumerateIterator<decltype(std::declval<Iterable>().begin())> enumerateIterator(Iterable& list) {
  return EnumerateIterator<decltype(std::declval<Iterable>().begin())>(list.begin(), list.end());
}

template<typename ResultContainer, typename Iterable>
ResultContainer enumerateConstruct(Iterable&& list) {
  ResultContainer res;
  for (auto el : enumerateIterator(list))
    res.push_back(move(el));

  return res;
}
于 2015-02-27T16:12:46.620 回答
3

C ++ 17 和结构化绑定使这看起来不错 - 肯定比一些丑陋的可变 lambda 和本地[i = 0](Element&) mutable或我在承认可能并非所有内容都应该硬塞到for_each() 等之前所做的任何事情要好。- 并且比其他需要具有for循环外范围的计数器的解决方案。

for (auto [it, end, i] = std::tuple{container.cbegin(), container.cend(), 0};
     it != end; ++it, ++i)
{
      // something that needs both `it` and `i`ndex
}

如果您经常使用此模式,您可以将其设为通用:

template <typename Container>
auto
its_and_idx(Container&& container)
{
    using std::begin, std::end;
    return std::tuple{begin(container), end(container), 0};
}

// ...

for (auto [it, end, i] = its_and_idx(foo); it != end; ++it, ++i)
{
    // something
}

C++ 标准提案P2164建议添加views::enumerate,这将提供一个范围的视图,为迭代它的用户提供对元素的引用和元素的索引。

我们提出了一个视图enumerate,其值类型为struct具有 2 个成员的 a indexvalue分别表示适应范围内元素的位置和值。

[ . . .]

此功能以某种形式存在于 Python、Rust、Go(支持该语言)以及许多 C++ 库中:ranges-v3follyboost::ranges( indexed)。

此功能的存在或缺乏是反复出现的 stackoverflow 问题的主题。

你看!我们很有名。

于 2020-05-23T11:31:57.833 回答
2

您还可以更优雅地使用自 C++11 以来可用的自动范围:

int i = 0;
for (auto& el : container){
    charges.at(counter) = el[0];
    aa.at(counter) = el[1];
    ++i;
}

不过,您仍然必须i手动计数。

于 2015-02-27T16:00:17.133 回答
0

这是一个基于宏的解决方案,它可能在简单性、编译时间和代码生成质量方面胜过大多数其他解决方案:

#include <iostream>

#define fori(i, ...) if(size_t i = -1) for(__VA_ARGS__) if(i++, true)

int main() {
    fori(i, auto const & x : {"hello", "world", "!"}) {
        std::cout << i << " " << x << std::endl;
    }
}

结果:

$ g++ -o enumerate enumerate.cpp -std=c++11 && ./enumerate 
0 hello
1 world
2 !
于 2019-01-12T03:56:45.420 回答
0

Tobias Widlund 写了一个很好的 MIT 许可的 Python 样式标头,仅枚举(虽然是 C++17):

GitHub

博客文章

真的很好用:

std::vector<int> my_vector {1,3,3,7};

for(auto [i, my_element] : en::enumerate(my_vector))
{
    // do stuff
}
于 2019-02-13T11:49:58.573 回答
0

Boost::Range从 1.56 开始支持此功能。

#include <boost/range/adaptor/indexed.hpp>
#include <boost/assign.hpp>
#include <iterator>
#include <iostream>
#include <vector>


int main(int argc, const char* argv[])
{
    using namespace boost::assign;
    using namespace boost::adaptors;

    std::vector<int> input;
    input += 10,20,30,40,50,60,70,80,90;

//  for (const auto& element : index(input, 0)) // function version
    for (const auto& element : input | indexed(0))      
    {
        std::cout << "Element = " << element.value()
                  << " Index = " << element.index()
                  << std::endl;
    }

    return 0;
}
于 2019-07-24T00:57:28.400 回答