简体   繁体   English

与Haskell的“ inits”等效的C ++ 11

[英]C++11 equivalent of Haskell's “inits”

Just a short question: 只是一个简短的问题:

Is there any C++11 equivalent of Haskell's inits ? Haskell的init有C ++ 11的等效项吗?

The inits function returns all initial segments of the argument, shortest first. inits函数返回参数的所有初始段,最短的开始。

I'd like to do sth. 我想做某事。 like 喜欢

reverse $ inits [1..10]

C++11 supports std::reverse , but i could not find sth. C ++ 11支持std :: reverse ,但是我找不到某事。 like std::inits . std :: inits一样

The List will be represented in C++ as a std::vector . 该列表将在C ++中以std :: vector表示

I think I got it working purely functional: 我认为我的工作完全是功能正常的:

#include <iostream>
#include <algorithm>
#include <vector>

using namespace std;

int main(void) {
  vector<int> nums = { 1,5,5,9,8,7,6 };

  auto inits = accumulate(nums.begin()+1, nums.end(), // Iterate between second and last element
                  vector< vector<int> >{ vector<int>{nums.front()}}, // Initialize accumulator
                  [] (vector< vector<int> > &acc, int j) { // Lambda constructing further elements
                        auto tmp = acc.back();
                        tmp.push_back(j);
                        acc.push_back( tmp );
                  });
  return 0;
}

Inits will be a vector of vectors of int's. Inits将是int向量的向量。

Everything without (visible) loops :-) 一切都没有(可见)循环:-)

For a random-access range (since you mention std::vector ), a range of successive slices is manageable. 对于随机访问范围(因为您提到了std::vector ),连续切片的范围是可管理的。 This will also work with forward and bidirectional ranges, although that will incur an additional linear cost when computing the distance. 这也适用于正向和双向范围,尽管在计算距离时会产生额外的线性成本。 With Boost.Range: 使用Boost.Range:

#include <boost/range/irange.hpp>
#include <boost/range/adaptor/sliced.hpp>
#include <boost/range/adaptor/transformed.hpp>

namespace R = boost::adaptors;

template<typename Range>
using range_difference_t = typename boost::range_difference<Range>::type;

namespace functors {

template<typename Range>
struct slice {
    using difference_type = range_difference_t<Range>;
    Range* range;

    explicit slice(Range& range)
        : range(&range)
    {}

    boost::sliced_range<Range> operator()(difference_type index) const
    {
        return R::slice(*range, static_cast<difference_type>(0), index);
    }
};

} // functors

template<typename Range>
using inits_type =
    boost::transformed_range<
        functors::slice<Range>,
        const boost::integer_range<range_difference_t<Range>>
    >;

// calling inits with rvalues is not supported on purpose
template<typename Range>
inits_type<Range> inits(Range& range)
{
    using diff_t = range_difference_t<Range>;
    return R::transform(
        // use boost::size instead of distance to restrict
        // inits to working efficiently on random-access ranges only
        boost::irange(static_cast<diff_t>(0), boost::distance(range) + static_cast<diff_t>(1)),
        functors::slice<Range> { range }
        );
}

Demo here . 演示在这里

This solution benefits greatly from C++14, leaving us with just: 该解决方案受益于C ++ 14,使我们仅需:

// same includes

template<typename Range>
auto inits(Range& range)
{
    namespace R = boost::adaptors;
    using diff_t = typename boost::range_difference<Range>::type;
    return R::transform(
        boost::irange(static_cast<diff_t>(0), boost::distance(range) + static_cast<diff_t>(1)),
        [range = &range](diff_t i) { return R::slice(*range, static_cast<diff_t>(0), i); }
        );
}

C++14 demo here . 这里的C ++ 14演示

As for a non-slicing solution (ie closer in spirit to the Haskell version), this would require writing iterators by hand, with 'interesting' lifetime considerations. 对于非切片解决方案(即在本质上更接近Haskell版本),这将需要手工编写迭代器,并考虑“有趣的”生命周期。 I would not recommend it. 我不推荐它。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM