Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Help with adapting split_view to take a length rather than a pattern. #277

Open
melton1968 opened this issue Feb 12, 2019 · 3 comments
Open

Comments

@melton1968
Copy link

I initially tried to get this functionality by combining off-the-shelf views, but I don't think that is possible.

So, I am trying to write a group_view that is analogous to split_view but takes a length rather than a pattern. After puzzling over the code for a long while, here is how I think I should proceed:

  1. Change all the construction paths to take a Length rather than a Pattern and store the Length in the view . The Length will necessarily be an Integral type. If the length

  2. Change outer_iterator::operator++ to move forward Length units at a time.

  3. Change outer_iterator::value_type to be something like subrange. Or, can I use subrange directly here?

Is there a better approach?

Any guidance is appreciated.

cheers,
mark

@CaseyCarter
Copy link
Owner

It may be helpful to look at the implementation of range-v3's chunk_view, which splits a range into a range of subranges of a runtime-specified size.

@melton1968
Copy link
Author

melton1968 commented Feb 12, 2019

So I have group_n (produces a range of ranges with each subrange containing n elements where n is given by a range) working for a simple test:

Note 1. v::chr::alpha() produces lowercase letters
Note 2. v::in_range(3, 7) produces integers in the open range (3,7)

auto generator = v::chr::alpha() | v::group_n(v::in_range(3, 7)) | v::take(20);
for (auto values : generator)
{
    for (auto value : values)
        cout << value;
     cout << Lendl;
}

produces:

icoheo
yzzzesz
ycmuuhd
kcx
...

A couple of questions:

  1. Is there any interest, once I have this cleaned up and tested, in taking this as a contribution?

  2. I am a little unsure about the following snippets of code from the implementation of outer_iterator (which follows very closely to split_view::outer_iterator with pattern replaced by length). Am I manipulating the iterators correctly in operator++?

    using subrange_iter_t = counted_iterator<iterator_t<Base>>;
    using value_type = subrange<subrange_iter_t, default_sentinel, subrange_kind::sized>;

    constexpr value_type operator*() const
    {
        auto iter_n = counted_iterator(current(), *parent_->length_.begin());
        auto rng = subrange{ iter_n, default_sentinel{} };
        return rng;
    }

    constexpr outer_iterator& operator++() {
        auto& cur = current();
        const auto end = __stl2::end(parent_->base_);

        index_t n = *parent_->length_.begin();
        ++(parent_->length_.begin());
        while (++cur != end and n > 0)
            --n;

        return *this;
    }

After this, I plan on implementing group which will be analogous to group_n but taking a predicate indicating where to break the groups rather than a count.

cheers,
mark

@melton1968
Copy link
Author

This is my implementation of group_n. It doesn't include any optimizations for forward_iterator, but otherwise is functional.

Example usage:

TEST(View, GroupNFixed)
{
    strings expected = { "abcd", "efgh", "ijkl", "mnop", "qrst",
                         "uvwx", "yzab", "cdef", "ghij", "klmn" };
    auto actual = v::iota(0)
        | v::transform([](auto i) { return char((i % 26) + 'a'); })
        | v::group_n(4)
        | v::take(10)
        | v::subrange_to<string>
        | r::to<vector>;
    EXPECT_EQ(actual, expected);
};
#pragma once
#include <stl2/type_traits.hpp>
#include <stl2/detail/fwd.hpp>
#include <stl2/detail/algorithm/mismatch.hpp>
#include <stl2/detail/concepts/object.hpp>
#include <stl2/detail/iterator/default_sentinel.hpp>
#include <stl2/detail/range/access.hpp>
#include <stl2/detail/range/concepts.hpp>
#include <stl2/detail/view/view_closure.hpp>
#include <stl2/view/all.hpp>
#include <stl2/view/single.hpp>
#include "core/common.h"

namespace std::experimental::ranges {

namespace detail::group_n {

template<class Range, class Count>
constexpr bool ViewReqs = View<Range>
    && View<Count>
    && Integral<iter_value_t<iterator_t<Count>>>;

template<InputRange Range, InputRange Count>
requires ViewReqs<Range, Count>
struct base
{
private:
    using subrange_iter_t = counted_iterator<iterator_t<Range>>;
    using value_type = subrange<subrange_iter_t, default_sentinel, subrange_kind::sized>;
    
    sentinel_t<Range> end_range_ {};
    iterator_t<Range> current_range_ {};
    iterator_t<Range> current_group_ {};
    iterator_t<Count> current_count_ {};
    index_t actual_count_ {};

protected:
    void reset_iterators(Range& range, Count& count)
    {
	end_range_ = __stl2::end(range);
	current_range_ = __stl2::begin(range);
	current_group_ = __stl2::begin(range);
	current_count_ = __stl2::begin(count);
	actual_count_ = 0;
    }
    
    auto end_range() const
    { return end_range_; }
    
    auto current_group() const
    { return current_group_; }
    
    value_type group_subrange() const
    {
	auto iter_n = counted_iterator{ current_group_, int(actual_count_) };
	return subrange{ iter_n, default_sentinel{} };
    }
    
    void advance_group() 
    { 
	current_group_ = current_range_;
	index_t max_count = *current_count_;
	actual_count_ = 0;
	while (current_range_ != end_range_ and max_count > 0)
	{
	    ++current_range_;
	    ++actual_count_;
	    --max_count;
	}
    }

    void advance_count()
    {
	auto iter = current_count_;
	++iter;
    }
};

}; // end ns detail::group_n

template<InputRange Range, InputRange Count> requires detail::group_n::ViewReqs<Range, Count>
struct group_n_view : public detail::group_n::base<Range, Count>
{
private:
    using Base = detail::group_n::base<Range, Count>;
    using Base::reset_iterators;
    using group_value_type = typename Base::value_type;
    struct group_iterator;

    Range range_ {};
    Count count_ {};
    
public:
    group_n_view() = default;
    
    constexpr group_n_view(Range range, Count count)
	: range_(std::move(range))
	, count_(std::move(count))
    {}
	
    constexpr group_n_view(Range range, Integral count)
	requires Constructible<Count, single_view<int>>
     	: range_(std::move(range))
     	, count_(std::move(single_view{count}))
    {}
	
    constexpr auto begin()
    {
	reset_iterators(range_, count_);
	return group_iterator{ *this };
    }
	
    constexpr auto end() const { return default_sentinel{}; }
};

template<class Range, class Count>
group_n_view(Range&&, Count&&) -> group_n_view<all_view<Range>, all_view<Count>>;

template<class Range, Integral Count>
group_n_view(Range&&, Count) -> group_n_view<all_view<Range>, single_view<Count>>;

template<InputRange Range, InputRange Count>
requires detail::group_n::ViewReqs<Range, Count>
struct group_n_view<Range, Count>::group_iterator
{
private:
    using Parent = group_n_view;
    Parent *parent_{ nullptr };

public:
    using iterator_category = __stl2::input_iterator_tag;
    using difference_type = iter_difference_t<iterator_t<Range>>;
    using value_type = group_n_view::group_value_type;
    
    group_iterator() = default;
    
    constexpr explicit  group_iterator(Parent& parent)
	: parent_(std::addressof(parent))
    { parent_->advance_group(); }

    constexpr auto operator*() const
    { return parent_->group_subrange(); }

    constexpr group_iterator& operator++()
    {
	parent_->advance_group();
	parent_->advance_count();
	return *this;
    }
    
    constexpr decltype(auto) operator++(int)
    { ++*this; }
    
    friend constexpr bool operator==(const group_iterator& x, default_sentinel)
    { return x.parent_->current_group() == x.parent_->end_range(); }
    
    friend constexpr bool operator==(default_sentinel x, const group_iterator& y)
    { return y == x; }
    
    friend constexpr bool operator!=(const group_iterator& x, default_sentinel y)
    { return !(x == y); }
    
    friend constexpr bool operator!=(default_sentinel x, const group_iterator& y)
    { return !(y == x);	}
};

namespace view {
struct __group_n_fn {
    template<class E, class F>
    constexpr auto operator()(E&& e, F&& f) const
	STL2_REQUIRES_RETURN(group_n_view{static_cast<E&&>(e), static_cast<F&&>(f)});
	
    template<CopyConstructible T>
    constexpr auto operator()(T&& t) const
    { return detail::view_closure{*this, std::forward<T>(t)}; }
};

inline constexpr __group_n_fn group_n {};
}; // end ns view

}; // end ns stl2

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants