libreria iterador español ejemplo cplusplus c++ algorithm loops stl c++14

c++ - iterador - Forma de STL para acceder a más elementos al mismo tiempo en un bucle sobre un contenedor



stl c++ español (4)

Esta es una implementación que mantiene una matriz de iteradores de tamaño N bajo el capó para producir una ventana deslizante:

namespace details { template<unsigned...>struct indexes { using type=indexes; }; template<unsigned max, unsigned... is>struct make_indexes:make_indexes<max-1, max-1, is...>{}; template<unsigned... is>struct make_indexes<0,is...>:indexes<is...>{}; template<unsigned max>using make_indexes_t=typename make_indexes<max>::type; template<bool b, class T=void> using enable_if_t=typename std::enable_if<b,T>::type; struct list_tag {}; struct from_iterator_tag {}; template<unsigned N, class Iterator> struct iterator_array { private: std::array<Iterator,N> raw; size_t index = 0; static Iterator to_elem(Iterator& it, Iterator end, bool advance=true) { if (it == end) return end; if (advance) return ++it; return it; } template< unsigned...Is> iterator_array( indexes<Is...>, from_iterator_tag, Iterator& it, Iterator end ): raw( {to_elem(it, end, false), (void(Is), to_elem(it,end))...} ) {} public: Iterator begin() const { return raw[index]; } Iterator end() const { return std::next(raw[(index+N-1)%N]); } void push_back( Iterator it ) { raw[index] = it; index = (index+1)%N; } iterator_array( from_iterator_tag, Iterator& it, Iterator end ):iterator_array( make_indexes<N-1>{}, from_iterator_tag{}, it, end ) {} iterator_array( iterator_array const& o )=default; iterator_array() = default; // invalid! iterator_array& operator=( iterator_array const& o )=delete; typedef decltype(*std::declval<Iterator>()) reference_type; reference_type operator[](std::size_t i)const{return *(raw[ (i+index)%N ]);} }; struct sentinal_tag {}; template<class I>using value_type_t=typename std::iterator_traits<I>::value_type; template<class I, unsigned N> class slide_iterator:public std::iterator< std::forward_iterator_tag, iterator_array<N,I>, iterator_array<N,I>*, iterator_array<N,I> const& > { I current; mutable bool bread = false; typedef iterator_array<N,I> value_type; mutable value_type data; void ensure_read() const { if (!bread) { data.push_back(current); } bread = true; } public: slide_iterator& operator++() { ensure_read(); ++current; bread=false; return *this; } slide_iterator operator++(int) { slide_iterator retval=*this; ++*this; return retval; } value_type const& operator*() const { ensure_read(); return data; } bool operator==(slide_iterator const& o){return current==o.current;} bool operator!=(slide_iterator const& o){return current!=o.current;} bool operator<(slide_iterator const& o){return current<o.current;} bool operator>(slide_iterator const& o){return current>o.current;} bool operator<=(slide_iterator const& o){return current<=o.current;} bool operator>=(slide_iterator const& o){return current>=o.current;} explicit slide_iterator( I start, I end ):current(start), bread(true), data(from_iterator_tag{}, current, end) {} explicit slide_iterator( sentinal_tag, I end ):current(end) {} }; } template<class Iterator, unsigned N> struct slide_range_t { using iterator=details::slide_iterator<Iterator, N>; iterator b; iterator e; slide_range_t( Iterator start, Iterator end ): b( start, end ), e( details::sentinal_tag{}, end ) {} slide_range_t( slide_range_t const& o )=default; slide_range_t() = delete; iterator begin() const { return b; } iterator end() const { return e; } }; template<unsigned N, class Iterator> slide_range_t< Iterator, N > slide_range( Iterator b, Iterator e ) { return {b,e}; }

ejemplo en vivo

Tenga en cuenta que los elementos de su rango de diapositivas son iterables. Una mejora adicional sería especializarse para iteradores de acceso aleatorio y solo almacenar el par de inicio / final en ese caso.

Uso de muestra:

int main() { std::vector<int> foo(33); for (int i = 0; i < foo.size(); ++i) foo[i]=i; for( auto&& r:slide_range<3>(foo.begin(), foo.end()) ) { for (int x : r) { std::cout << x << ","; } std::cout << "/n"; } // your code goes here return 0; }

¿Es posible reescribir este ciclo sin formato?

vector<double> v { ... }; for (size_t i = 1; i<v.size(); ++i) { v[i]*=v[i-1]; }

o el aún más críptico:

for (auto i = v.begin()+1; i<v.end(); ++i) { (*i) *= *(i-1); }

(y similar, tal vez acceder también v [i-2], ...) de una manera más STLish?

¿Hay otras formas iguales o mejores (tanto en estilo como en rendimiento) que las anteriores?


La manera más directa que puedo imaginar:

std::partial_sum(std::begin(v), std::end(v), std::begin(v), std::multiplies<double>());

Ejemplo:

#include <iostream> #include <vector> #include <iterator> #include <numeric> #include <functional> int main() { std::vector<double> v{ 1.0, 2.0, 3.0, 4.0 }; std::partial_sum(std::begin(v), std::end(v), std::begin(v), std::multiplies<double>()); std::copy(std::begin(v), std::end(v), std::ostream_iterator<double>(std::cout, " ")); }

Salida:

1 2 6 24

Enlace de demostración en vivo.


Puedes hacer eso con std::transform , la sobrecarga que toma dos secuencias de entrada:

int container[] = {1,2,3}; std::transform( std::begin(container), std::end(container) - 1, std::begin(container) + 1, std::begin(container) + 1, [](auto a, auto b) { return a * b; } );

Pero el ciclo codificado a mano es mucho más legible.


Si desea una forma genérica de hacer ventanas deslizantes en lugar de una forma STL-ish no transferible para responder a su problema en particular, podría considerar las siguientes tonterías ridículas:

#include <array> #include <cstddef> #include <memory> #include <tuple> namespace detail { template<std::size_t, typename> class slide_iterator; } template<std::size_t N, typename I> detail::slide_iterator<N, I> slide_begin(const I&); template<std::size_t N, typename I> detail::slide_iterator<N, I> slide_end(const I&); namespace detail { template<std::size_t N, typename T, typename... Args> struct repeat { typedef typename repeat<N - 1, T, T, Args...>::type type; template<typename I> type operator()(const I& it, Args&... args) const { auto jt = it; return repeat<N - 1, T, T, Args...>()(++jt, args..., *it); } }; template<typename T, typename... Args> struct repeat<0, T, Args...> { typedef std::tuple<Args&...> type; template<typename I> type operator()(const I&, Args&... args) const { return type(args...); } }; template<std::size_t N, typename I /* forward iterator */> class slide_iterator { public: typedef slide_iterator iterator; typedef decltype(*I{}) reference; typedef typename repeat<N, reference>::type window_tuple; slide_iterator() = default; ~slide_iterator() = default; slide_iterator(const iterator& it) = default; iterator& operator=(const iterator& it) = default; window_tuple operator*() const { return repeat<N, reference>()(first_); } iterator& operator++() { // prefix ++first_; ++last_; return *this; } iterator operator++(int) { // postfix auto tmp{*this}; operator++(); return tmp; } friend void swap(iterator& lhs, iterator& rhs) { swap(lhs.first_, rhs.first_); swap(lhs.last_, rhs.last_); swap(lhs.dirty_, rhs.dirty_); swap(lhs.window_, rhs.window_); } friend bool operator==(const iterator& lhs, const iterator& rhs) { return lhs.last_ == rhs.last_; } friend bool operator!=(const iterator& lhs, const iterator& rhs) { return !operator==(lhs, rhs); } friend iterator slide_begin<N, I>(const I& it); friend iterator slide_end<N, I>(const I& it); private: I first_; I last_; // for equality only }; template<typename T, std::size_t N> struct slide_helper { T& t; auto begin() -> decltype(slide_begin<N>(t.begin())) { return slide_begin<N>(t.begin()); } auto end() -> decltype(slide_end<N>(t.end())) { return slide_end<N>(t.end()); } }; } // ::detail // note it is undefined to call slide_begin<N>() on an iterator which cannot // be incremented at least N - 1 times template<std::size_t N, typename I> detail::slide_iterator<N, I> slide_begin(const I& it) { detail::slide_iterator<N, I> r; r.first_ = r.last_ = it; std::advance(r.last_, N - 1); return r; } template<std::size_t N, typename I> detail::slide_iterator<N, I> slide_end(const I& it) { detail::slide_iterator<N, I> r; r.last_ = it; return r; } template<std::size_t N, typename T> detail::slide_helper<T, N> slide(T& t) { return {t}; }

Ejemplo de uso:

#include <iostream> #include <vector> int main() { std::vector<int> v{1, 2, 3, 4}; /* helper for for (auto it = slide_begin<2>(v.begin()), et = slide_end<2>(v.end()); it != et ... BLAH BLAH BLAH */ for (const auto& t : slide<2>(v)) { std::get<1>(t) *= std::get<0>(t); } for (const auto& i : v) { std::cout << i << std::endl; } }