boostorg / mp11

C++11 metaprogramming library
http://boost.org/libs/mp11
242 stars 66 forks source link

Requesting zip_tail / zip_tail_with #54

Closed brevzin closed 4 years ago

brevzin commented 4 years ago

I'm not sure what the right name for these should be (the Python itertools module has an example for the first one that is named pairwise). These are fairly trivial to implement:

template <typename L, template <typename...> class F>
using mp_zip_tail_with = mp_transform<F, mp_pop_back<L>, mp_pop_front<L>>;

template <typename L>
using mp_zip_tail = mp_zip_tail_with<L, mp_list>;

But seem useful enough that should come batteries included (and possibly there's a more efficient implementation that doesn't rely on mp_pop_back).

On that note, mp_transform currently requires all the lists to have the same length. I think that's worth adding to the documentation (not really obvious, and the error if you get it wrong isn't particularly informative). Is it worth having a version of this that just takes the shortest list as zip?

pdimov commented 4 years ago

zip_tail? Where does this name come from?

pdimov commented 4 years ago

And what is the use case?

brevzin commented 4 years ago

zip_tail? Where does this name come from?

In Haskell it's like \x -> zip x (tail x) or ap zip tail. You're zip-ing a list with its own tail.

And what is the use case?

Any time you need to do some operation on consecutive pairs of elements in a single list. Checking if a list is sorted, for instance, is mp_rename<mp_zip_tail_with<L, mp_less>, mp_all>. C++ calls this adjacent_difference (which is such a bad name that people use binary std::transform anyway).

pdimov commented 4 years ago

I like the horrible name, although not the difference part. mp_adjacent_transform perhaps. mp_adjacent is shorter but a bit cryptic. mp_pairwise_transform also works, but adjacent is more precise.

I can't think of a way to avoid the mp_pop_back here.

brevzin commented 4 years ago

I like the horrible name, although not the difference part.

That's the horrible part! 😂

pdimov commented 4 years ago

Since the result has one fewer element, applying this transform to a list with one element yields an empty list. But, as written, applying it to a list with zero elements is a substitution failure, which breaks the simple is_sorted above. Yielding an empty list in this case as well will arguably be more usable, but less consistent. Thoughts?

brevzin commented 4 years ago

Yeah, I think yielding an empty list is the right thing. It's good enough for Haskell:

> zip_tail = \x -> zip x (tail x)
> zip_tail []
=> []
> zip_tail [1]
=> []
> zip_tail [1, 2]
=> [(1,2)]

Even though tail [] throws:

> tail []
*** Exception: Prelude.tail: empty list
pdimov commented 4 years ago

Eh, mp_transform<F, L> but mp_adjacent_transform<L, F>... Why are trivial things always so hard.

brevzin commented 4 years ago

Eh, mp_transform<F, L> but mp_adjacent_transform<L, F>... Why are trivial things always so hard.

Yeah for proposing adding new views to C++23, I think the names we're going for are adjacent and adjacent_transform rather than zip_tail and zip_tail_with. That'll be in the October mailing.

pdimov commented 4 years ago

My problem is with the inconsistent argument order though.

pdimov commented 4 years ago

mp_pairwise_fold it is.