Есть ли эквивалент цикла enumerate
на основе диапазона из Python в C ++? Я представляю себе что-то подобное.
enumerateLoop (auto counter, auto el, container) {
charges.at(counter) = el[0];
aa.at(counter) = el[1];
}
Можно ли это сделать с помощью шаблонов или макросов?
Я знаю, что могу просто использовать старый цикл for-loop и повторять, пока не дойду до container.size()
. Но мне интересно, как это решить с помощью шаблонов или макросов.
ИЗМЕНИТЬ
Я немного поигрался с итераторами повышения после подсказки в комментариях. У меня есть еще одно рабочее решение на 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
9 ответов
Я кое-что написал для этого некоторое время назад.
По сути, вам нужно обернуть итератор и дать ему парную семантику.
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;
}
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;
}
Тобиас Видлунд написал красивый заголовок в стиле Python, лицензированный MIT, только enumerate (хотя C ++ 17):
Действительно приятно использовать:
std::vector<int> my_vector {1,3,3,7};
for(auto [i, my_element] : en::enumerate(my_vector))
{
// do stuff
}
Вот решение на основе макросов, которое, вероятно, превосходит большинство других по простоте, времени компиляции и качеству генерации кода:
#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 !
Этот вопрос помечен как c ++ 14, но я хотел бы опубликовать ответ для c ++ 20, потому что есть действительно красивое решение, которое я опубликовал здесь
В C ++ 17 и структурированных привязках это выглядит нормально - определенно лучше, чем некрасивые изменяемые лямбды с локальным [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
с двумя членамиindex
иvalue
, представляющими соответственно положение и значение элементов в адаптированном диапазоне.[. . .]
Эта функция в той или иной форме существует в Python, Rust, Go (с поддержкой языка) и во многих библиотеках C ++:
ranges-v3
,folly
,boost::ranges
(indexed
).Наличие или отсутствие этой функции является предметом повторяющихся вопросов о переполнении стека.
Эй смотри! Мы знамениты.
Вы также можете более элегантно использовать автоматические диапазоны, доступные с C ++ 11:
int i = 0;
for (auto& el : container){
charges.at(counter) = el[0];
aa.at(counter) = el[1];
++i;
}
Однако вам все равно придется пересчитывать i
вручную.
Для этого есть решение, предшествующее C ++ 11: boost.range.indexed. К сожалению, он не работает с циклами for на основе диапазона C ++ 11, только с подробными циклами старого стиля. Однако с C ++ 17 это должно быть (почти) таким же простым, как в python, используя структурированные привязки
Тогда должно быть возможно реализовать что-то, что работает следующим образом:
for (auto& [n,x] : enumerate(vec)) x = n;
Итак, немного подождем;)
Перечисление нескольких переменных было идиомой, начиная с C. Единственная сложность заключается в том, что вы не можете объявить обе переменные в инициализаторе цикла for.
int index;
for (auto p = container.begin(), index = 0; p != container.end(); ++p, ++index)
Я не думаю, что это может быть проще (или мощнее), чем это.
Похожие вопросы
Связанные вопросы
Новые вопросы
c++
C ++ - это язык программирования общего назначения. Первоначально он был разработан как расширение C и имеет аналогичный синтаксис, но теперь это совершенно другой язык. Используйте этот тег для вопросов о коде (который должен быть) скомпилирован с помощью компилятора C ++. Используйте тег для конкретной версии для вопросов, связанных с конкретной версией стандарта [C ++ 11], [C ++ 14], [C ++ 17], [C ++ 20] или [C ++ 23] и т. Д. .