Step/Stride Iterator for use with std::minmax_element

孤街浪徒 提交于 2021-02-18 16:56:31

问题


I have a 1D float array which represents a m *n (rows and columns) table of float values. My requirement is to find a min/max element for each row and column. For rows I can easily do it by using std::minmax_element by specifying a range of n elements. But for columns I need to use a stride iterator as elements are placed are not contiguous but placed at a step interval of n. Is there a standard iterator in boost/STL that can be used. The other option is to write my own version of it. What is the best course ?


回答1:


Two ways (among many) of doing it are using range-v3 (or boost::range) and boost::iterator.


With range-v3, it's immediate:

#include <iostream>
#include <range/v3/all.hpp>

using namespace ranges;

int main() {
    std::vector<std::size_t> src{1, 2, 3, 4, 5, 6, 7};
    const auto min_max = minmax(src | view::stride(3));
    std::cout << min_max.first << " " << min_max.second << std::endl;
}

In boost::range, use boost::adaptors::strided.

#include <boost/range/adaptor/strided.hpp>
#include <boost/range/algorithm/copy.hpp>
#include <boost/assign.hpp>
#include <boost/range/algorithm.hpp>
#include <algorithm>
#include <iostream>
#include <vector>

int main()
{
    using namespace boost::adaptors;
    using namespace boost::assign;

    std::vector<int> input;
    int arr[] = {1, 2, 3, 4, 5, 6, 7};

    auto str = std::make_pair(&arr[0], &arr[8]) | strided(3);
    std::cout << *boost::range::min_element(str) << " " << *boost::range::max_element(str) << std::endl;
}

Note the following:

  1. A range can be defined by a pair of iterators, so I used that here although a simpler form is possible (it fits your use case of heap-allocated C-style arrays).

  2. Unfortunately, this sub-library doesn't seem to have min-max (or at least I couldn't find it), so there are two calls, one for min, and one for max.


Given a random-access iterator, it's not very difficult to build a strided forward boost::iterator_facade:

#include <iostream>
#include <vector>
#include <iterator>
#include <cstddef>
#include <algorithm>
#include <boost/iterator/iterator_facade.hpp>

template<typename It>
class stride_iterator :
    public boost::iterator_facade<
        stride_iterator<It>,
        typename std::iterator_traits<It>::value_type,
        boost::forward_traversal_tag> {
public:
    stride_iterator() = default;
    stride_iterator(It it, It end_it, std::size_t stride) : 
        m_it{it}, m_end_it{end_it}, m_stride{stride} 
    {}

private:
    friend class boost::iterator_core_access;

    void increment() { 
        if(std::distance(m_it, m_end_it) < m_stride) {
            m_it = m_end_it;
            return;
        }
        std::advance(m_it, m_stride);
    }

    bool equal(const stride_iterator<It> &other) const {
        return m_it == other.m_it;
    }

    typename std::iterator_traits<It>::value_type &dereference() const { 
        return *m_it; }

    It m_it, m_end_it;
    std::size_t m_stride;
};

This should be enough for std::minmax_element. (Adding a bit of logic, the decrement and advance members, and changing the tag, would make it into a random-access iterator too.)

int main() {
    using vec_t = std::vector<int>;
    vec_t v{1, 2, 3, 4, 5, 6, 7};
    stride_iterator<vec_t::iterator> b{std::begin(v), std::end(v), 3}, e{std::end(v), std::end(v), 3};
    auto min_max = std::minmax_element(b, e);
    std::cout << *min_max.first << " " << *min_max.second << std::endl;
}


来源:https://stackoverflow.com/questions/39814331/step-stride-iterator-for-use-with-stdminmax-element

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!