Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

STL way to access more elements at the same time in a loop over a container

Is it possible to rewrite this raw loop:

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

or the even more cryptic:

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

(and similar, maybe accessing also v[i-2], ...) in a more STLish way?

Are there other forms which are equal or better (both in style and performances) than the ones above?

like image 978
DarioP Avatar asked Sep 03 '14 09:09

DarioP


3 Answers

The most STLish way I can imagine:

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

Example:

#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, " "));
}

Output:

1 2 6 24 

Live demo link.

like image 122
Piotr Skotnicki Avatar answered Oct 23 '22 17:10

Piotr Skotnicki


You can do that with std::transform, the overload that takes two input sequences:

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

But the hand-coded loop is much more readable.

like image 10
Maxim Egorushkin Avatar answered Oct 23 '22 18:10

Maxim Egorushkin


If you want a generic way to do sliding windows rather than a non-transferable STL-ish way to answer your particular problem, you could consider the following ridiculous nonsense:

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

Example usage:

#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;
  }
}
like image 7
STU Avatar answered Oct 23 '22 16:10

STU