一种通过索引过滤范围的方法,只从过滤的索引中获取min_element?

时间:2015-06-22 09:13:56

标签: c++ templates c++11 boost c++14

在对这个问题is-there-a-way-to-iterate-over-at-most-n-elements-using-range-based-for-loop的评论中还有另外一个问题 - 这是否可能在容器上有“索引视图”,即具有过滤掉某些索引的子范围。

此外,我遇到了一个问题,即从过滤掉某些索引的范围中找到最小值。

即。是否有可能用std和/或boost算法,过滤器替换下面的代码 - 使其更具可读性和可维护性:

template <typename Range, typename IndexPredicate>
auto findMin(const Range& range, IndexPredicate ipred) 
     -> boost::optional<typename Range::value_type>
{
    bool found = false;
    typename Range::value_type minValue{};
    for (std::size_t i = 0; i < range.size(); ++i)
    {
        if (not ipred(i))
            continue;
        if (not found)
        {
            minValue = range[i];
            found = true;
        }
        else if (minValue > range[i])
        {
            minValue = range[i];
        }
    }
    if (found)
    {
        return minValue;
    }
    else
    {
        return boost::none;
    }
}

就像这样使用:

#include <iostream>
#include <vector>

int main() {
    std::vector<float> ff = {1.2,-1.2,2.3,-2.3};
    auto onlyEvenIndex = [](auto i){ return (i&1) == 0;};

    auto minValue = findMin(ff, onlyEvenIndex);
    std::cout << *minValue << std::endl;
}

3 个答案:

答案 0 :(得分:6)

使用最近的标准range-v3 proposal

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

int main() 
{
    std::vector<float> rng = {1.2,-1.2,2.3,-2.3};

    auto even_indices = 
        ranges::view::iota(0ul, rng.size()) | 
        ranges::view::filter([](auto i) { return !(i & 1); })
    ;
    auto min_ind = *ranges::min_element(
        even_indices, [&rng](auto L, auto R) { 
        return rng[L] < rng[R]; 
    });
    std::cout << rng[min_ind];
}

Live Example。请注意,语法大致类似于Boost.Range,但完全改进以利用C ++ 14(广义lambdas,自动返回类型推导等)。

答案 1 :(得分:3)

解决这个问题的方法是超越C ++过滤范围的自然方式。我的意思是 - 我们需要过滤索引的范围,而不是值的范围。但是从哪里获得了索引范围?有办法获得索引范围 - boost::irange。所以 - 看到这个:

#include <boost/range/irange.hpp>
#include <boost/range/adaptor/filtered.hpp>
#include <boost/range/algorithm/min_element.hpp>
#include <functional>

template <typename Range, typename IndexPredicate>
auto findMin(const Range& range, IndexPredicate ipred) -> boost::optional<typename Range::value_type>
{
    using boost::adaptors::filtered;
    using boost::irange;

    auto filtered_indexes = irange(0u, range.size()) | filtered(std::function<bool(std::size_t)>{ipred});

使用提升范围的一个缺点是they have problems to use raw lambdas - 因此我需要使用std::function

Nest步骤就像使用boost::min_element一样简单 - 唯一要记住的是你应该比较值。不只是索引:

auto lessByValue = [&range] (auto lhs, auto rhs) 
{ 
      return range[lhs] < range[rhs]; 
};

最后的步骤:

auto foundMin = boost::min_element(filtered_indexes, lessByValue);
if (foundMin != std::end(filtered_indexes))
    return range[*foundMin];
else 
    return boost::none;

答案 2 :(得分:2)

从早期问题的this answer开始。 (可选)在该问题中增加所描述的任务。

增加mylist = [[2,3,None],[None,3,4]] for x in mylist: for y in x: if y is None: y = '' print mylist 以支持步幅模板参数indexT:将size_t stride=1替换为++t;

std::advance(t,stride);添加到ItB base() const { return b+**a(); }(以后版本)。

添加indexing_iterator这是一个编译时间跨度的索引范围。

撰写适用于两个template<size_t stride> using index_stride_range=range<indexT<size_t, stride>>;的{​​{1}}。输出是步幅intersect的{​​{1}}。解决它开始的地方是另一项工作,但只有高中数学。请注意,index_stride_range是一种带有index_stride_range的{​​{1}},因此此升级也适用于gcd(lhs_stride, rhs_stride)

升级index_rangeindex_stride_range作为第一个参数。实现是相同的(除了依赖升级的stride=1)。

index_range,这是index_filter,从index_stride_rangeintersect或某些(在简单情况下基本上为0到无穷大) - 额外的数学是找到适合every_nth_index<N>(offset)的偏移量+ k *步幅的最大数字。

现在我们得到:

index_stride_range

我们有一个迭代器。 offset会将迭代器返回到包含size_t(-(1+(abs(offset))%stride) - (size_t(-(1+(abs(offset)%stride)))%stride)元素的size_t(不是auto filtered = index_filter( every_nth_index<2>(), container ); auto it = (std::min)( filtered.begin(), filtered.end() ); ,这是不同的。)