C ++ 11相当于Haskell" inits"

时间:2014-04-09 14:26:28

标签: haskell c++11 functional-programming

只是一个简短的问题:

是否有任何C ++ 11相当于Haskell的inits

  

inits函数返回参数的所有初始段,   最短的。

我想做某事。像

reverse $ inits [1..10]

C ++ 11支持 std::reverse ,但我找不到某事。比如 std :: inits

List将在C ++中表示为 std :: vector

2 个答案:

答案 0 :(得分:2)

我认为我的工作纯粹是功能性的:

#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将是int的矢量向量。

没有(可见)循环的一切: - )

答案 1 :(得分:1)

对于随机访问范围(因为您提到std::vector),一系列连续切片是可管理的。这也适用于前向和双向范围,但在计算距离时会产生额外的线性成本。使用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

这个解决方案从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

对于非切片解决方案(即精神上与Haskell版本更接近),这需要手工编写迭代器,并考虑“有趣”的生命周期。我不推荐它。