diff --git a/example/list/group.cpp b/example/list/group.cpp new file mode 100644 index 000000000..436b54b46 --- /dev/null +++ b/example/list/group.cpp @@ -0,0 +1,26 @@ +/* +@copyright Louis Dionne 2014 +Distributed under the Boost Software License, Version 1.0. +(See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt) + */ + +#include +#include +#include +#include +using namespace boost::hana; + + +int main() { + //! [main] + BOOST_HANA_CONSTANT_ASSERT( + group(list(int_<1>, long_<1>, type, char_<'x'>, char_<'x'>)) + == + list( + list(int_<1>, long_<1>), + list(type), + list(char_<'x'>, char_<'x'>) + ) + ); + //! [main] +} diff --git a/example/list/group_by.cpp b/example/list/group_by.cpp new file mode 100644 index 000000000..939f49e45 --- /dev/null +++ b/example/list/group_by.cpp @@ -0,0 +1,27 @@ +/* +@copyright Louis Dionne 2014 +Distributed under the Boost Software License, Version 1.0. +(See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt) + */ + +#include +#include +#include +#include +using namespace boost::hana; + + +int main() { + //! [main] + BOOST_HANA_CONSTEXPR_ASSERT( + group_by(equal ^on^ decltype_, + list(1, 2, 3, 'x', 'y', 4.4, 5.5) + ) + == list( + list(1, 2, 3), + list('x', 'y'), + list(4.4, 5.5) + ) + ); + //! [main] +} diff --git a/example/list/span.cpp b/example/list/span.cpp new file mode 100644 index 000000000..2c3d8fdbf --- /dev/null +++ b/example/list/span.cpp @@ -0,0 +1,37 @@ +/* +@copyright Louis Dionne 2014 +Distributed under the Boost Software License, Version 1.0. +(See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt) + */ + +#include +#include +#include +#include +#include +#include +using namespace boost::hana; + + +int main() { + //! [main] + BOOST_HANA_CONSTEXPR_LAMBDA auto xs = list(int_<1>, int_<2>, int_<3>, int_<4>); + BOOST_HANA_CONSTANT_ASSERT( + span(xs, _ < int_<3>) + == + pair(list(int_<1>, int_<2>), list(int_<3>, int_<4>)) + ); + + BOOST_HANA_CONSTANT_ASSERT( + span(xs, _ < int_<0>) + == + pair(list(), xs) + ); + + BOOST_HANA_CONSTANT_ASSERT( + span(xs, _ < int_<5>) + == + pair(xs, list()) + ); + //! [main] +} diff --git a/include/boost/hana/comparable/comparable.hpp b/include/boost/hana/comparable/comparable.hpp index 7be38911c..e83d45d63 100644 --- a/include/boost/hana/comparable/comparable.hpp +++ b/include/boost/hana/comparable/comparable.hpp @@ -20,6 +20,7 @@ namespace boost { namespace hana { //! @ingroup group-typeclasses //! The `Comparable` type class defines equality and inequality. //! + //! @anchor equivalence_relation //! ### Laws //! `equal` must define an equivalence relation. In other words, for all //! `a`, `b`, `c` of comparable data types, diff --git a/include/boost/hana/list/list.hpp b/include/boost/hana/list/list.hpp index b7fcb83ff..cf079d8e3 100644 --- a/include/boost/hana/list/list.hpp +++ b/include/boost/hana/list/list.hpp @@ -44,7 +44,7 @@ namespace boost { namespace hana { //! Actually, they might be just the same. Check this out. //! - Implement the following methods: //! - `intersperse`, `intercalate`, `transpose`, `subsequences` - //! - `split_at`, `span`, `break`, `group_by`, `group`, `inits`, `tails` + //! - `split_at`, `break`, `inits`, `tails` //! - Consider implementing the following methods: //! - `nub_by`, `nub`, `delete_by`, `insert` //! - `set_difference_by`, `set_union_by`, `set_intersection_by` @@ -116,6 +116,55 @@ namespace boost { namespace hana { >::filter_impl(xs, predicate); }; + //! Group the elements of a list into subgroups of adjacent elements that + //! are "equal" with respect to a predicate. + //! @relates List + //! + //! Specifically, `group_by` takes a list and returns a list of lists + //! such that the concatenation of the result is equal to the argument. + //! Moreover, each sublist contains only elements for which the predicate + //! is satisfied when applied to two adjacent elements. + //! + //! + //! @param predicate + //! A binary function called as `predicate(x, y)`, where `x` and `y` + //! are _adjacent_ elements in the list, and returning a `Logical` + //! representing whether both elements should be in the same group + //! (sublist) of the result. The result returned by `predicate` has + //! to be a [compile-time](@ref Logical_terminology) `Logical`. Also, + //! `predicate` has to define an [equivalence relation] + //! (@ref equivalence_relation) as defined by the `Comparable` type class. + //! + //! @param xs + //! The list to split into groups. + //! + //! + //! ### Example + //! @snippet example/list/group_by.cpp main + BOOST_HANA_CONSTEXPR_LAMBDA auto group_by = [](auto predicate, auto xs) { + return List::instance< + datatype_t + >::group_by_impl(predicate, xs); + }; + + //! Group the elements of a list into subgroups of adjacent equal elements. + //! @relates List + //! + //! Specifically, `group(xs)` is equivalent to `group_by(equal, xs)`. + //! For this method to work, comparing adjacent elements have to return + //! a [compile-time](@ref Logical_terminology) `Logical`. + //! + //! @param xs + //! The list to split into groups. + //! + //! ### Example + //! @snippet example/list/group.cpp main + BOOST_HANA_CONSTEXPR_LAMBDA auto group = [](auto xs) { + return List::instance< + datatype_t + >::group_impl(xs); + }; + //! Remove the last element of a non-empty list. //! @relates List //! @@ -409,6 +458,42 @@ namespace boost { namespace hana { >::sort_by_impl(predicate, xs); }; + //! Return a `Product` containing the longest prefix of a list satisfying + //! a predicate, and the rest of the list. + //! @relates List + //! + //! The first element of the returned `Product` is a list for which all + //! elements satisfy the given predicate. The second element of the + //! returned `Product` is a list containing the remainder of the argument. + //! Both or either lists may be empty, depending on the input argument. + //! Specifically, `span(xs, predicate)` is equivalent to + //! @code + //! span(xs, predicate) == pair( + //! take_while(predicate, xs), + //! drop_while(predicate, xs) + //! ) + //! @endcode + //! except that `pair` may be an arbitrary `Product`. + //! + //! + //! @param xs + //! The list to break into two parts. + //! + //! @param predicate + //! A function called as `predicate(x)`, where `x` is an element of the + //! list, and returning a `Logical. In the current implementation of the + //! library, `predicate` has to return a [compile-time] + //! (@ref Logical_terminology) `Logical`. + //! + //! + //! ### Example + //! @snippet example/list/span.cpp main + BOOST_HANA_CONSTEXPR_LAMBDA auto span = [](auto xs, auto predicate) { + return List::instance< + datatype_t + >::span_impl(xs, predicate); + }; + //! Return a list containing the first `n` elements of a list. //! @relates List //! diff --git a/include/boost/hana/list/mcd.hpp b/include/boost/hana/list/mcd.hpp index 373374133..b417e92f2 100644 --- a/include/boost/hana/list/mcd.hpp +++ b/include/boost/hana/list/mcd.hpp @@ -49,6 +49,26 @@ namespace boost { namespace hana { return foldr(xs, nil, go); } + template + static constexpr auto group_by_impl(Pred pred, Xs xs_) { + return eval_if(is_empty(xs_), + [](auto) { return nil; }, + [=](auto _) { + auto x = _(head)(xs_); + auto xs = _(tail)(xs_); + auto ys_zs = span(xs, [=](auto y) { return pred(x, y); }); + auto ys = first(ys_zs); + auto zs = second(ys_zs); + return cons(cons(x, ys), group_by_impl(pred, zs)); + } + ); + } + + template + static constexpr auto group_impl(Xs xs) { + return group_by(equal, xs); + } + template static constexpr auto init_impl(Xs xs) { return eval_if(is_empty(tail(xs)), @@ -188,6 +208,28 @@ namespace boost { namespace hana { ); } + template + static constexpr auto span_impl(Xs xs, Pred pred) { + return eval_if(is_empty(xs), + [=](auto _) { return pair(nil, nil); }, + [=](auto _) { + auto x = _(head)(xs); + auto xs_ = _(tail)(xs); + return eval_if(pred(x), + [=](auto _) { + auto ys_zs = span_impl(xs_, _(pred)); + auto ys = first(ys_zs); + auto zs = second(ys_zs); + return pair(cons(x, ys), zs); + }, + [=](auto _) { + return pair(nil, xs); + } + ); + } + ); + } + template static constexpr auto take_impl(N n, Xs xs) { return eval_if(or_(is_empty(xs), equal(n, int_<0>)), diff --git a/test/list/typeclass/group.cpp b/test/list/typeclass/group.cpp new file mode 100644 index 000000000..4f2a5a76b --- /dev/null +++ b/test/list/typeclass/group.cpp @@ -0,0 +1,43 @@ +/* +@copyright Louis Dionne 2014 +Distributed under the Boost Software License, Version 1.0. +(See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt) + */ + +#include + +#include +#include +#include + +#include +using namespace boost::hana; + + +// group requires the elements to be compile-time comparable +template +constexpr auto x = int_; + +template +void test() { + BOOST_HANA_CONSTEXPR_LAMBDA auto list = detail::minimal::list; + + BOOST_HANA_CONSTANT_ASSERT(group(list()) == list()); + + BOOST_HANA_CONSTANT_ASSERT(group(list(x<0>)) == list(list(x<0>))); + + BOOST_HANA_CONSTANT_ASSERT(group(list(x<0>, x<0>)) == list(list(x<0>, x<0>))); + BOOST_HANA_CONSTANT_ASSERT(group(list(x<0>, x<1>)) == list(list(x<0>), list(x<1>))); + + BOOST_HANA_CONSTANT_ASSERT(group(list(x<0>, x<0>, x<0>)) == list(list(x<0>, x<0>, x<0>))); + BOOST_HANA_CONSTANT_ASSERT(group(list(x<0>, x<0>, x<1>)) == list(list(x<0>, x<0>), list(x<1>))); + BOOST_HANA_CONSTANT_ASSERT(group(list(x<0>, x<1>, x<0>)) == list(list(x<0>), list(x<1>), list(x<0>))); + BOOST_HANA_CONSTANT_ASSERT(group(list(x<1>, x<0>, x<0>)) == list(list(x<1>), list(x<0>, x<0>))); + + BOOST_HANA_CONSTANT_ASSERT(group(list(x<0>, x<0>, x<1>, x<1>)) == list(list(x<0>, x<0>), list(x<1>, x<1>))); + BOOST_HANA_CONSTANT_ASSERT(group(list(x<0>, x<0>, x<1>, x<1>, x<2>, x<2>, x<2>)) == list(list(x<0>, x<0>), list(x<1>, x<1>), list(x<2>, x<2>, x<2>))); +} + +int main() { + test>(); +} diff --git a/test/list/typeclass/group_by.cpp b/test/list/typeclass/group_by.cpp new file mode 100644 index 000000000..5dccb45ce --- /dev/null +++ b/test/list/typeclass/group_by.cpp @@ -0,0 +1,58 @@ +/* +@copyright Louis Dionne 2014 +Distributed under the Boost Software License, Version 1.0. +(See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt) + */ + +#include + +#include +#include +#include +#include +#include +using namespace boost::hana; + + +struct EqClass; +template struct X { using hana_datatype = EqClass; }; +template struct Y { using hana_datatype = EqClass; }; +template constexpr X x{}; +template constexpr Y y{}; + +namespace boost { namespace hana { + template <> + struct Comparable::instance : Comparable::equal_mcd { + template + static constexpr auto equal_impl(T, U) + { return false_; } + + template + static constexpr auto equal_impl(T, T) + { return true_; } + }; +}} + +constexpr struct _predicate { + template class X, template class Y> + constexpr auto operator()(X, Y) const + { return bool_<(i == j)>; } +} pred{}; + + +template +void test() { + BOOST_HANA_CONSTEXPR_LAMBDA auto list = detail::minimal::list; + + BOOST_HANA_CONSTANT_ASSERT(group_by(pred, list()) == list()); + + BOOST_HANA_CONSTANT_ASSERT(group_by(pred, list(x<0>)) == list(list(x<0>))); + BOOST_HANA_CONSTANT_ASSERT(group_by(pred, list(x<0>, y<0>)) == list(list(x<0>, y<0>))); + BOOST_HANA_CONSTANT_ASSERT(group_by(pred, list(x<0>, y<0>, x<1>)) == list(list(x<0>, y<0>), list(x<1>))); + BOOST_HANA_CONSTANT_ASSERT(group_by(pred, list(x<0>, y<0>, x<1>, y<1>)) == list(list(x<0>, y<0>), list(x<1>, y<1>))); + BOOST_HANA_CONSTANT_ASSERT(group_by(pred, list(x<0>, y<0>, x<1>, y<1>, y<0>)) == list(list(x<0>, y<0>), list(x<1>, y<1>), list(y<0>))); +} + +int main() { + test>(); +} diff --git a/test/list/typeclass/span.cpp b/test/list/typeclass/span.cpp new file mode 100644 index 000000000..17ae6a409 --- /dev/null +++ b/test/list/typeclass/span.cpp @@ -0,0 +1,52 @@ +/* +@copyright Louis Dionne 2014 +Distributed under the Boost Software License, Version 1.0. +(See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt) + */ + +#include + +#include +#include +#include +#include + +#include +using namespace boost::hana; + + +BOOST_HANA_CONSTEXPR_LAMBDA auto is = [](auto x) { + return [=](auto y) { return equal(x, y); }; +}; + +// the predicate needs to be compile-time +template +constexpr auto x = int_; + +template +void test() { + BOOST_HANA_CONSTEXPR_LAMBDA auto list = detail::minimal::list; + BOOST_HANA_CONSTEXPR_LAMBDA auto p = detail::minimal::product<>; + + constexpr auto z = x<999>; + + BOOST_HANA_CONSTANT_ASSERT(span(list(), is(z)) == p(list(), list())); + + BOOST_HANA_CONSTANT_ASSERT(span(list(x<0>), is(z)) == p(list(), list(x<0>))); + BOOST_HANA_CONSTANT_ASSERT(span(list(z), is(z)) == p(list(z), list())); + + BOOST_HANA_CONSTANT_ASSERT(span(list(x<0>, z), is(z)) == p(list(), list(x<0>, z))); + BOOST_HANA_CONSTANT_ASSERT(span(list(z, x<0>), is(z)) == p(list(z), list(x<0>))); + BOOST_HANA_CONSTANT_ASSERT(span(list(x<0>, x<1>), is(z)) == p(list(), list(x<0>, x<1>))); + + BOOST_HANA_CONSTANT_ASSERT(span(list(x<0>, x<1>, x<2>), is(z)) == p(list(), list(x<0>, x<1>, x<2>))); + BOOST_HANA_CONSTANT_ASSERT(span(list(z, x<1>, x<2>), is(z)) == p(list(z), list(x<1>, x<2>))); + BOOST_HANA_CONSTANT_ASSERT(span(list(x<0>, z, x<2>), is(z)) == p(list(), list(x<0>, z, x<2>))); + BOOST_HANA_CONSTANT_ASSERT(span(list(z, z, x<2>), is(z)) == p(list(z, z), list(x<2>))); + BOOST_HANA_CONSTANT_ASSERT(span(list(z, z, z), is(z)) == p(list(z, z, z), list())); +} + +int main() { + test>(); + (void)is; +}