diff --git a/example/sequence.cpp b/example/sequence.cpp index 105cfd989..dc795d25a 100644 --- a/example/sequence.cpp +++ b/example/sequence.cpp @@ -423,31 +423,29 @@ BOOST_HANA_CONSTEXPR_CHECK( }{ -//! [unfoldl] -BOOST_HANA_CONSTEXPR_LAMBDA auto f = [](auto x) { - return if_(x == int_<0>, nothing, just(pair(x - int_<1>, x))); -}; - +//! [unfold] BOOST_HANA_CONSTANT_CHECK( - unfoldl(f, int_<10>) + unfold.left(int_<10>, [](auto x) { + return if_(x == int_<0>, + nothing, + just(pair(x - int_<1>, x)) + ); + }) == tuple_c ); -//! [unfoldl] - -}{ - -//! [unfoldr] -BOOST_HANA_CONSTEXPR_LAMBDA auto f = [](auto x) { - return if_(x == int_<0>, nothing, just(pair(x, x - int_<1>))); -}; BOOST_HANA_CONSTANT_CHECK( - unfoldr(f, int_<10>) + unfold.right(int_<10>, [](auto x) { + return if_(x == int_<0>, + nothing, + just(pair(x, x - int_<1>)) + ); + }) == tuple_c ); -//! [unfoldr] +//! [unfold] }{ diff --git a/include/boost/hana/fwd/sequence.hpp b/include/boost/hana/fwd/sequence.hpp index ae70a7d93..670bcc421 100644 --- a/include/boost/hana/fwd/sequence.hpp +++ b/include/boost/hana/fwd/sequence.hpp @@ -1247,78 +1247,26 @@ namespace boost { namespace hana { constexpr _take_while take_while{}; #endif - //! Dual to `fold.left` for sequences. + //! Dual operation to `fold` for sequences. //! @relates Sequence //! - //! While `fold.left` reduces a structure to a summary value, `unfoldl` - //! builds a sequence from a seed value and a function. In some cases, - //! `unfoldl` can undo a `fold.left` operation: + //! While `fold` reduces a structure to a summary value, `unfold` builds + //! a sequence from a seed value and a function. As explained in the + //! documentation for `fold`, there are several different flavors of + //! folds. In particular, there are folds which are left associative + //! and folds which are right associative. Similarly, there are two + //! versions of `unfold`; one which builds the sequence from the left, + //! and another one which builds the sequence from the right. Those + //! two variants are accessible through `unfold.left` and + //! `unfold.right`, respectively. Also note that for convenience, + //! `unfold` is an alias to `unfold.left`. + //! + //! + //! ### Fun fact + //! In some cases, `unfold` can undo a `fold` operation: //! @code - //! unfoldl(g, fold.left(xs, z, f)) - //! @endcode - //! - //! if the following holds - //! @code - //! g(f(y, x)) == just(pair(y, x)) - //! g(z) == nothing - //! @endcode - //! - //! - //! @tparam S - //! The data type of the sequence to build up. - //! - //! @param f - //! A function called as `f(init)`, where `init` is an initial value, - //! and returning - //! 1. `nothing` if it is done producing the sequence. - //! 2. `just(pair(init, x))` if it isn't, where `init` is the new - //! initial value used in the next call to `f` and `x` is an - //! element to be appended to the resulting sequence. Also note - //! that `pair` may actually be replaced by any `Product`. - //! - //! @param init - //! An initial value to build the sequence from. - //! - //! - //! Example - //! ------- - //! @snippet example/sequence.cpp unfoldl -#ifdef BOOST_HANA_DOXYGEN_INVOKED - template - constexpr auto unfoldl = [](auto&& f, auto&& init) -> decltype(auto) { - return tag-dispatched; - }; -#else - template - struct unfoldl_impl; - - template - struct _unfoldl { -#ifdef BOOST_HANA_CONFIG_CHECK_DATA_TYPES - static_assert(_models{}, - "hana::unfoldl(f, initial) requires S to be a Sequence"); -#endif - template - constexpr decltype(auto) operator()(F&& f, Initial&& initial) const { - return unfoldl_impl::apply( - detail::std::forward(f), - detail::std::forward(initial) - ); - } - }; - - template - constexpr _unfoldl unfoldl{}; -#endif - - //! Dual to `fold.right` for sequences. - //! @relates Sequence - //! - //! While `fold.right` reduces a structure to a summary value, `unfoldr` - //! builds a sequence from a seed value and a function. In some cases, - //! `unfoldr` can undo a `fold.right` operation: - //! @code - //! unfoldr(g, fold.right(xs, z, f)) + //! unfold.left(fold.left(xs, z, f), g) == xs + //! unfold.right(fold.right(xs, z, f), g) == xs //! @endcode //! //! if the following holds @@ -1328,51 +1276,90 @@ namespace boost { namespace hana { //! @endcode //! //! + //! Signature + //! --------- + //! Given a Sequence data type `S`, an initial value `init` of data type + //! `I`, an arbitrary Product `P` and a function \f$ f : I \to P(I, T) \f$, + //! `unfold.left` has the following signature: + //! \f[ + //! \mathrm{unfold}_S.\mathrm{left} : I \times (I \to P(I, T)) \to S(T) + //! \f] + //! + //! Given a function \f$ f : I \to P(T, I) \f$ instead, `unfold.right` + //! has the following signature: + //! \f[ + //! \mathrm{unfold}_S.\mathrm{right} : I \times (I \to P(T, I)) \to S(T) + //! \f] + //! //! @tparam S //! The data type of the sequence to build up. //! + //! @param init + //! An initial value to build the sequence from. + //! //! @param f //! A function called as `f(init)`, where `init` is an initial value, //! and returning - //! 1. `nothing` if it is done producing the sequence. - //! 2. `just(pair(x, init))` if it isn't, where `init` is the new - //! initial value used in a recursive call to `f` and `x` is an - //! element prepended to the resulting sequence. Also note that - //! `pair` may actually be replaced by any `Product`. - //! - //! @param init - //! An initial value to build the list from. + //! 1. `nothing` if it is done producing the sequence. + //! 2. otherwise, `just(pair(init, x))` for `unfold.left` and + //! `just(pair(x, init))` for `unfold.right`, where `init` is the + //! new initial value used in the next call to `f` and `x` is an + //! element to be appended to the resulting sequence. Also note that + //! `pair` may actually be replaced by any `Product`. //! //! //! Example //! ------- - //! @snippet example/sequence.cpp unfoldr + //! @snippet example/sequence.cpp unfold #ifdef BOOST_HANA_DOXYGEN_INVOKED template - constexpr auto unfoldr = [](auto&& f, auto&& init) -> decltype(auto) { - return tag-dispatched; - }; + constexpr auto unfold = see documentation; #else template - struct unfoldr_impl; + struct unfold_left_impl; + + template + struct unfold_right_impl; template - struct _unfoldr { -#ifdef BOOST_HANA_CONFIG_CHECK_DATA_TYPES - static_assert(_models{}, - "hana::unfoldr(f, initial) requires S to be a Sequence"); -#endif - template - constexpr decltype(auto) operator()(F&& f, Initial&& initial) const { - return unfoldr_impl::apply( - detail::std::forward(f), - detail::std::forward(initial) + struct _unfold_left { + template + constexpr decltype(auto) operator()(Initial&& initial, F&& f) const { + return unfold_left_impl::apply( + static_cast(initial), + static_cast(f) ); } }; template - constexpr _unfoldr unfoldr{}; + struct _unfold_right { + template + constexpr decltype(auto) operator()(Initial&& initial, F&& f) const { + return unfold_right_impl::apply( + static_cast(initial), + static_cast(f) + ); + } + }; + + template + struct _unfold : _unfold_left { + #ifdef BOOST_HANA_CONFIG_CHECK_DATA_TYPES + static_assert(_models{}, + "hana::unfold requires S to be a Sequence"); + #endif + + static constexpr _unfold_left left{}; + static constexpr _unfold_right right{}; + }; + template + constexpr _unfold_left _unfold::left; + template + constexpr _unfold_right _unfold::right; + + template + constexpr _unfold unfold{}; #endif //! Unzip a sequence of sequences. diff --git a/include/boost/hana/sequence.hpp b/include/boost/hana/sequence.hpp index eed41d7ff..309c4f3a4 100644 --- a/include/boost/hana/sequence.hpp +++ b/include/boost/hana/sequence.hpp @@ -766,57 +766,63 @@ namespace boost { namespace hana { }; ////////////////////////////////////////////////////////////////////////// - // unfoldl + // unfold.left ////////////////////////////////////////////////////////////////////////// template - struct unfoldl_impl : unfoldl_impl> { }; + struct unfold_left_impl : unfold_left_impl> { }; template - struct unfoldl_impl> : default_ { - struct unfoldl_helper { + struct unfold_left_impl> : default_ { + struct unfold_left_helper { template constexpr decltype(auto) operator()(F&& f, P&& p) const { return hana::append( - unfoldl_impl::apply(static_cast(f), - hana::first(static_cast(p))), + unfold_left_impl::apply( + hana::first(static_cast(p)), + static_cast(f) + ), hana::second(static_cast(p)) ); } }; - template - static constexpr decltype(auto) apply(F&& f, Init&& init) { + template + static constexpr decltype(auto) apply(Init&& init, F&& f) { + decltype(auto) elt = f(static_cast(init)); return hana::maybe(empty(), - hana::partial(unfoldl_helper{}, f), - f(static_cast(init)) + hana::partial(unfold_left_helper{}, static_cast(f)), + static_cast(elt) ); } }; ////////////////////////////////////////////////////////////////////////// - // unfoldr + // unfold.right ////////////////////////////////////////////////////////////////////////// template - struct unfoldr_impl : unfoldr_impl> { }; + struct unfold_right_impl : unfold_right_impl> { }; template - struct unfoldr_impl> : default_ { - struct unfoldr_helper { + struct unfold_right_impl> : default_ { + struct unfold_right_helper { template constexpr decltype(auto) operator()(F&& f, P&& p) const { return hana::prepend( hana::first(static_cast(p)), - unfoldr_impl::apply(static_cast(f), - hana::second(static_cast(p))) + unfold_right_impl::apply( + hana::second(static_cast(p)), + static_cast(f) + ) ); } }; - template - static constexpr decltype(auto) apply(F&& f, Init&& init) { + template + static constexpr decltype(auto) apply(Init&& init, F&& f) { + decltype(auto) elt = f(static_cast(init)); return hana::maybe(empty(), - hana::partial(unfoldr_helper{}, f), - f(static_cast(init)) + hana::partial(unfold_right_helper{}, static_cast(f)), + static_cast(elt) ); } }; diff --git a/test/laws/sequence.hpp b/test/laws/sequence.hpp index 750a4b5f2..8e19ce1d3 100644 --- a/test/laws/sequence.hpp +++ b/test/laws/sequence.hpp @@ -1007,121 +1007,138 @@ namespace boost { namespace hana { namespace test { #elif !defined(BOOST_HANA_TEST_SEQUENCE_PART) || BOOST_HANA_TEST_SEQUENCE_PART == 3 ////////////////////////////////////////////////////////////////// - // unfoldl + // unfold.left ////////////////////////////////////////////////////////////////// { - auto prod = minimal_product; - _injection<0> f{}; - auto stop_at = [=](auto stop) { - return [=](auto x) { - return hana::if_(hana::equal(stop, x), - hana::nothing, - hana::just(prod(hana::succ(x), f(x))) - ); + auto prod = minimal_product; + _injection<0> f{}; + auto stop_at = [=](auto stop) { + return [=](auto x) { + return hana::if_(hana::equal(stop, x), + hana::nothing, + hana::just(prod(hana::succ(x), f(x))) + ); + }; }; - }; - BOOST_HANA_CONSTANT_CHECK(equal( - unfoldl(stop_at(int_<0>), int_<0>), - list() - )); - BOOST_HANA_CONSTANT_CHECK(equal( - unfoldl(stop_at(int_<1>), int_<0>), - list(f(int_<0>)) - )); - BOOST_HANA_CONSTANT_CHECK(equal( - unfoldl(stop_at(int_<2>), int_<0>), - list(f(int_<1>), f(int_<0>)) - )); - BOOST_HANA_CONSTANT_CHECK(equal( - unfoldl(stop_at(int_<3>), int_<0>), - list(f(int_<2>), f(int_<1>), f(int_<0>)) - )); - BOOST_HANA_CONSTANT_CHECK(equal( - unfoldl(stop_at(int_<4>), int_<0>), - list(f(int_<3>), f(int_<2>), f(int_<1>), f(int_<0>)) - )); + BOOST_HANA_CONSTANT_CHECK(equal( + unfold.left(int_<0>, stop_at(int_<0>)), + list() + )); + BOOST_HANA_CONSTANT_CHECK(equal( + unfold.left(int_<0>, stop_at(int_<1>)), + list(f(int_<0>)) + )); + BOOST_HANA_CONSTANT_CHECK(equal( + unfold.left(int_<0>, stop_at(int_<2>)), + list(f(int_<1>), f(int_<0>)) + )); + BOOST_HANA_CONSTANT_CHECK(equal( + unfold.left(int_<0>, stop_at(int_<3>)), + list(f(int_<2>), f(int_<1>), f(int_<0>)) + )); + BOOST_HANA_CONSTANT_CHECK(equal( + unfold.left(int_<0>, stop_at(int_<4>)), + list(f(int_<3>), f(int_<2>), f(int_<1>), f(int_<0>)) + )); + + // make sure unfold is equivalent to unfold.left + BOOST_HANA_CONSTANT_CHECK(equal( + unfold(int_<0>, stop_at(int_<0>)), + unfold.left(int_<0>, stop_at(int_<0>)) + )); + BOOST_HANA_CONSTANT_CHECK(equal( + unfold(int_<0>, stop_at(int_<3>)), + unfold.left(int_<0>, stop_at(int_<3>)) + )); + BOOST_HANA_CONSTANT_CHECK(equal( + unfold(int_<0>, stop_at(int_<4>)), + unfold.left(int_<0>, stop_at(int_<4>)) + )); } ////////////////////////////////////////////////////////////////// - // unfoldr + // unfold.right ////////////////////////////////////////////////////////////////// { - auto prod = minimal_product; - _injection<0> f{}; - auto stop_at = [=](auto stop) { - return [=](auto x) { - return hana::if_(hana::equal(stop, x), + auto prod = minimal_product; + _injection<0> f{}; + auto stop_at = [=](auto stop) { + return [=](auto x) { + return hana::if_(hana::equal(stop, x), + nothing, + hana::just(prod(f(x), hana::succ(x))) + ); + }; + }; + + BOOST_HANA_CONSTANT_CHECK(hana::equal( + unfold.right(int_<0>, stop_at(int_<0>)), + list() + )); + BOOST_HANA_CONSTANT_CHECK(hana::equal( + unfold.right(int_<0>, stop_at(int_<1>)), + list(f(int_<0>)) + )); + BOOST_HANA_CONSTANT_CHECK(hana::equal( + unfold.right(int_<0>, stop_at(int_<2>)), + list(f(int_<0>), f(int_<1>)) + )); + BOOST_HANA_CONSTANT_CHECK(hana::equal( + unfold.right(int_<0>, stop_at(int_<3>)), + list(f(int_<0>), f(int_<1>), f(int_<2>)) + )); + BOOST_HANA_CONSTANT_CHECK(hana::equal( + unfold.right(int_<0>, stop_at(int_<4>)), + list(f(int_<0>), f(int_<1>), f(int_<2>), f(int_<3>)) + )); + } + + ////////////////////////////////////////////////////////////////// + // Make sure unfolds can be reversed under certain conditions. + ////////////////////////////////////////////////////////////////// + { + auto prod = minimal_product; + auto z = eq<999>{}; + auto f = prod; + auto g = [=](auto k) { + return if_(equal(k, z), nothing, - hana::just(prod(f(x), hana::succ(x))) + just(k) ); }; - }; - BOOST_HANA_CONSTANT_CHECK(hana::equal( - unfoldr(stop_at(int_<0>), int_<0>), - list() - )); - BOOST_HANA_CONSTANT_CHECK(hana::equal( - unfoldr(stop_at(int_<1>), int_<0>), - list(f(int_<0>)) - )); - BOOST_HANA_CONSTANT_CHECK(hana::equal( - unfoldr(stop_at(int_<2>), int_<0>), - list(f(int_<0>), f(int_<1>)) - )); - BOOST_HANA_CONSTANT_CHECK(hana::equal( - unfoldr(stop_at(int_<3>), int_<0>), - list(f(int_<0>), f(int_<1>), f(int_<2>)) - )); - BOOST_HANA_CONSTANT_CHECK(hana::equal( - unfoldr(stop_at(int_<4>), int_<0>), - list(f(int_<0>), f(int_<1>), f(int_<2>), f(int_<3>)) - )); - } - - ////////////////////////////////////////////////////////////////// - // Make sure unfold{r,l} can be reversed under certain conditions. - ////////////////////////////////////////////////////////////////// - { - auto prod = minimal_product; - auto z = eq<999>{}; - auto f = prod; - auto g = [=](auto k) { - return if_(equal(k, z), nothing, just(k)); - }; - - // Make sure the special conditions are met - BOOST_HANA_CONSTANT_CHECK(equal( - g(z), - nothing - )); - BOOST_HANA_CONSTANT_CHECK(equal( - g(f(eq<0>{}, z)), - just(prod(eq<0>{}, z)) - )); - BOOST_HANA_CONSTANT_CHECK(equal( - g(f(z, eq<0>{})), - just(prod(z, eq<0>{})) - )); - - // Make sure the reversing works - auto lists = list( - list(), - list(eq<0>{}), - list(eq<0>{}, eq<1>{}), - list(eq<0>{}, eq<1>{}, eq<2>{}) - ); - for_each(lists, [=](auto xs) { + // Make sure the special conditions are met BOOST_HANA_CONSTANT_CHECK(equal( - unfoldl(g, fold.left(xs, z, f)), - xs + g(z), + nothing )); BOOST_HANA_CONSTANT_CHECK(equal( - unfoldr(g, fold.right(xs, z, f)), - xs + g(f(eq<0>{}, z)), + just(prod(eq<0>{}, z)) )); - }); + BOOST_HANA_CONSTANT_CHECK(equal( + g(f(z, eq<0>{})), + just(prod(z, eq<0>{})) + )); + + // Make sure the reversing works + auto lists = list( + list(), + list(eq<0>{}), + list(eq<0>{}, eq<1>{}), + list(eq<0>{}, eq<1>{}, eq<2>{}) + ); + for_each(lists, [=](auto xs) { + BOOST_HANA_CONSTANT_CHECK(equal( + unfold.left(fold.left(xs, z, f), g), + xs + )); + BOOST_HANA_CONSTANT_CHECK(equal( + unfold.right(fold.right(xs, z, f), g), + xs + )); + }); } //////////////////////////////////////////////////////////////////