Fully document algorithm.hh
This commit is contained in:
parent
6cdf31d42b
commit
45b5eb2c0e
|
@ -1,6 +1,18 @@
|
||||||
/* Algorithms for libostd.
|
/** @addtogroup Ranges
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
|
||||||
|
/** @file algorithm.hh
|
||||||
*
|
*
|
||||||
* This file is part of libostd. See COPYING.md for futher information.
|
* @brief Generic algorithms for working with ranges.
|
||||||
|
*
|
||||||
|
* This file provides various algorithms that work with ranges, including
|
||||||
|
* partitioning, sorting, comparison, iteration, finding, filling, folding,
|
||||||
|
* mapping, filtering and others. It's roughly equivalent to the C++ header
|
||||||
|
* `algorithm`, with many of the algorithms being just range-based versions
|
||||||
|
* of the iterator ones, but it also provides different custom algorithms.
|
||||||
|
*
|
||||||
|
* @copyright See COPYING.md in the project tree for further information.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef OSTD_ALGORITHM_HH
|
#ifndef OSTD_ALGORITHM_HH
|
||||||
|
@ -16,6 +28,10 @@
|
||||||
|
|
||||||
namespace ostd {
|
namespace ostd {
|
||||||
|
|
||||||
|
/** @addtogroup Ranges
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
|
||||||
/* partitioning */
|
/* partitioning */
|
||||||
|
|
||||||
/** @brief Partitions a range.
|
/** @brief Partitions a range.
|
||||||
|
@ -744,6 +760,15 @@ inline auto find_one_of(R &&values) {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @brief Counts the number of occurences of `v` in `range`.
|
||||||
|
*
|
||||||
|
* Iterates the range and each time `v` is encountered (using the `==`
|
||||||
|
* operator) a counter is incremented. This counter is then returned.
|
||||||
|
*
|
||||||
|
* The `range` is at least ostd::input_range_tag.
|
||||||
|
*
|
||||||
|
* @see ostd::count_if(), ostd::count_if_not()
|
||||||
|
*/
|
||||||
template<typename R, typename T>
|
template<typename R, typename T>
|
||||||
inline range_size_t<R> count(R range, T const &v) {
|
inline range_size_t<R> count(R range, T const &v) {
|
||||||
range_size_t<R> ret = 0;
|
range_size_t<R> ret = 0;
|
||||||
|
@ -755,6 +780,10 @@ inline range_size_t<R> count(R range, T const &v) {
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @brief A pipeable version of ostd::count().
|
||||||
|
*
|
||||||
|
* The `v` is forwarded.
|
||||||
|
*/
|
||||||
template<typename T>
|
template<typename T>
|
||||||
inline auto count(T &&v) {
|
inline auto count(T &&v) {
|
||||||
return [v = std::forward<T>(v)](auto &obj) mutable {
|
return [v = std::forward<T>(v)](auto &obj) mutable {
|
||||||
|
@ -762,6 +791,15 @@ inline auto count(T &&v) {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @brief Counts the number of elements matching `pred` in `range.
|
||||||
|
*
|
||||||
|
* Iterates the range and each time `pred(range.front())` returns true,
|
||||||
|
* a counter is incremented. This counter is then returned.
|
||||||
|
*
|
||||||
|
* The `range` is at least ostd::input_range_tag.
|
||||||
|
*
|
||||||
|
* @see ostd::count(), ostd::count_if_not()
|
||||||
|
*/
|
||||||
template<typename R, typename P>
|
template<typename R, typename P>
|
||||||
inline range_size_t<R> count_if(R range, P pred) {
|
inline range_size_t<R> count_if(R range, P pred) {
|
||||||
range_size_t<R> ret = 0;
|
range_size_t<R> ret = 0;
|
||||||
|
@ -773,13 +811,26 @@ inline range_size_t<R> count_if(R range, P pred) {
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename F>
|
/** @brief A pipeable version of ostd::count_if().
|
||||||
inline auto count_if(F &&func) {
|
*
|
||||||
return [func = std::forward<F>(func)](auto &obj) mutable {
|
* The `pred` is forwarded.
|
||||||
return count_if(obj, std::forward<F>(func));
|
*/
|
||||||
|
template<typename P>
|
||||||
|
inline auto count_if(P &&pred) {
|
||||||
|
return [pred = std::forward<P>(pred)](auto &obj) mutable {
|
||||||
|
return count_if(obj, std::forward<P>(pred));
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @brief Counts the number of elements not matching `pred` in `range.
|
||||||
|
*
|
||||||
|
* Iterates the range and each time `!pred(range.front())` returns true,
|
||||||
|
* a counter is incremented. This counter is then returned.
|
||||||
|
*
|
||||||
|
* The `range` is at least ostd::input_range_tag.
|
||||||
|
*
|
||||||
|
* @see ostd::count(), ostd::count_if()
|
||||||
|
*/
|
||||||
template<typename R, typename P>
|
template<typename R, typename P>
|
||||||
inline range_size_t<R> count_if_not(R range, P pred) {
|
inline range_size_t<R> count_if_not(R range, P pred) {
|
||||||
range_size_t<R> ret = 0;
|
range_size_t<R> ret = 0;
|
||||||
|
@ -791,17 +842,28 @@ inline range_size_t<R> count_if_not(R range, P pred) {
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename F>
|
/** @brief A pipeable version of ostd::count_if_not().
|
||||||
inline auto count_if_not(F &&func) {
|
*
|
||||||
return [func = std::forward<F>(func)](auto &obj) mutable {
|
* The `pred` is forwarded.
|
||||||
return count_if_not(obj, std::forward<F>(func));
|
*/
|
||||||
|
template<typename P>
|
||||||
|
inline auto count_if_not(P &&pred) {
|
||||||
|
return [pred = std::forward<P>(pred)](auto &obj) mutable {
|
||||||
|
return count_if_not(obj, std::forward<P>(pred));
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @brief Checks if two ranges have equal contents.
|
||||||
|
*
|
||||||
|
* Both ranges are at least ostd::input_range_tag. If one range becomes
|
||||||
|
* empty before the other does, false is returned. If the expression
|
||||||
|
* `!(range1.front() == range2.front())` is true in any iteration,
|
||||||
|
* false is also returned. Otherwise true is returned.
|
||||||
|
*/
|
||||||
template<typename R>
|
template<typename R>
|
||||||
inline bool equal(R range1, R range2) {
|
inline bool equal(R range1, R range2) {
|
||||||
for (; !range1.empty(); range1.pop_front()) {
|
for (; !range1.empty(); range1.pop_front()) {
|
||||||
if (range2.empty() || (range1.front() != range2.front())) {
|
if (range2.empty() || !(range1.front() == range2.front())) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
range2.pop_front();
|
range2.pop_front();
|
||||||
|
@ -809,6 +871,10 @@ inline bool equal(R range1, R range2) {
|
||||||
return range2.empty();
|
return range2.empty();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @brief A pipeable version of ostd::equal().
|
||||||
|
*
|
||||||
|
* The `range` is forwarded as the second range.
|
||||||
|
*/
|
||||||
template<typename R>
|
template<typename R>
|
||||||
inline auto equal(R &&range) {
|
inline auto equal(R &&range) {
|
||||||
return [range = std::forward<R>(range)](auto &obj) mutable {
|
return [range = std::forward<R>(range)](auto &obj) mutable {
|
||||||
|
@ -818,12 +884,29 @@ inline auto equal(R &&range) {
|
||||||
|
|
||||||
/* algos that modify ranges or work with output ranges */
|
/* algos that modify ranges or work with output ranges */
|
||||||
|
|
||||||
|
/** @brief Copies all elements from `irange` to `orange`.
|
||||||
|
*
|
||||||
|
* The `irange` is at least ostd::input_range_tag. The `orange` is
|
||||||
|
* an output range. The ostd::range_put_all() function is used to
|
||||||
|
* perform the copy. it respects ADL and therefore any per-type
|
||||||
|
* overloads of ostd::range_put_all.
|
||||||
|
*
|
||||||
|
* @see ostd::copy_if(), ostd::copy_if_not()
|
||||||
|
*/
|
||||||
template<typename R1, typename R2>
|
template<typename R1, typename R2>
|
||||||
inline R2 copy(R1 irange, R2 orange) {
|
inline R2 copy(R1 irange, R2 orange) {
|
||||||
range_put_all(orange, irange);
|
range_put_all(orange, irange);
|
||||||
return orange;
|
return orange;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @brief Copies elements of `irange` matching `pred` into `orange`.
|
||||||
|
*
|
||||||
|
* The `irange` (at least ostd::input_range_tag) is iterated and if
|
||||||
|
* the expression `pred(irange.front())` is true, the element is
|
||||||
|
* inserted (like `orange.put(irange.front())`).
|
||||||
|
*
|
||||||
|
* @see ostd::copy(), ostd::copy_if_not()
|
||||||
|
*/
|
||||||
template<typename R1, typename R2, typename P>
|
template<typename R1, typename R2, typename P>
|
||||||
inline R2 copy_if(R1 irange, R2 orange, P pred) {
|
inline R2 copy_if(R1 irange, R2 orange, P pred) {
|
||||||
for (; !irange.empty(); irange.pop_front()) {
|
for (; !irange.empty(); irange.pop_front()) {
|
||||||
|
@ -834,6 +917,14 @@ inline R2 copy_if(R1 irange, R2 orange, P pred) {
|
||||||
return orange;
|
return orange;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @brief Copies elements of `irange` not matching `pred` into `orange`.
|
||||||
|
*
|
||||||
|
* The `irange` (at least ostd::input_range_tag) is iterated and if
|
||||||
|
* the expression `!pred(irange.front())` is true, the element is
|
||||||
|
* inserted (like `orange.put(irange.front())`).
|
||||||
|
*
|
||||||
|
* @see ostd::copy(), ostd::copy_if()
|
||||||
|
*/
|
||||||
template<typename R1, typename R2, typename P>
|
template<typename R1, typename R2, typename P>
|
||||||
inline R2 copy_if_not(R1 irange, R2 orange, P pred) {
|
inline R2 copy_if_not(R1 irange, R2 orange, P pred) {
|
||||||
for (; !irange.empty(); irange.pop_front()) {
|
for (; !irange.empty(); irange.pop_front()) {
|
||||||
|
@ -844,16 +935,30 @@ inline R2 copy_if_not(R1 irange, R2 orange, P pred) {
|
||||||
return orange;
|
return orange;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename R1, typename R2>
|
/** @brief Reverses the order of the given range in-place.
|
||||||
inline R2 move(R1 irange, R2 orange) {
|
*
|
||||||
for (; !irange.empty(); irange.pop_front()) {
|
* The range must be at least ostd::bidirectional_range_tag. The range
|
||||||
orange.put(std::move(irange.front()));
|
* must also meet the conditions of ostd::is_range_element_swappable.
|
||||||
}
|
*
|
||||||
return orange;
|
* Equivalent to the following:
|
||||||
}
|
*
|
||||||
|
* ~~~{.cc}
|
||||||
|
* while (!range.empty()) {
|
||||||
|
* using std::swap;
|
||||||
|
* swap(range.front(), range.back());
|
||||||
|
* range.pop_front();
|
||||||
|
* range.pop_back();
|
||||||
|
* }
|
||||||
|
* ~~~
|
||||||
|
*
|
||||||
|
* @see ostd::reverse_copy()
|
||||||
|
*/
|
||||||
template<typename R>
|
template<typename R>
|
||||||
inline void reverse(R range) {
|
inline void reverse(R range) {
|
||||||
|
static_assert(
|
||||||
|
is_range_element_swappable<R>,
|
||||||
|
"The range element accessors must allow swapping"
|
||||||
|
);
|
||||||
while (!range.empty()) {
|
while (!range.empty()) {
|
||||||
using std::swap;
|
using std::swap;
|
||||||
swap(range.front(), range.back());
|
swap(range.front(), range.back());
|
||||||
|
@ -862,6 +967,24 @@ inline void reverse(R range) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @brief Reverses the order of the given range into `orange`.
|
||||||
|
*
|
||||||
|
* Iterates `irange` backwards, putting each item into `orange`. The
|
||||||
|
* `irange` has to be at least ostd::bidirectional_range_tag.
|
||||||
|
*
|
||||||
|
* Equivalent to the following:
|
||||||
|
*
|
||||||
|
* ~~~{.cc}
|
||||||
|
* while (!irange.empty()) {
|
||||||
|
* orange.put(irange.back());
|
||||||
|
* irange.pop_back();
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* return orange;
|
||||||
|
* ~~~
|
||||||
|
*
|
||||||
|
* @see ostd::reverse()
|
||||||
|
*/
|
||||||
template<typename R1, typename R2>
|
template<typename R1, typename R2>
|
||||||
inline R2 reverse_copy(R1 irange, R2 orange) {
|
inline R2 reverse_copy(R1 irange, R2 orange) {
|
||||||
for (; !irange.empty(); irange.pop_back()) {
|
for (; !irange.empty(); irange.pop_back()) {
|
||||||
|
@ -870,6 +993,13 @@ inline R2 reverse_copy(R1 irange, R2 orange) {
|
||||||
return orange;
|
return orange;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @brief Fills the given input range with `v`.
|
||||||
|
*
|
||||||
|
* Iterates over `range` and assigns `v` to each element. The elements
|
||||||
|
* must therefore be actual lvalue references so they can be assigned to.
|
||||||
|
*
|
||||||
|
* @see ostd::generate(), ostd::iota()
|
||||||
|
*/
|
||||||
template<typename R, typename T>
|
template<typename R, typename T>
|
||||||
inline void fill(R range, T const &v) {
|
inline void fill(R range, T const &v) {
|
||||||
for (; !range.empty(); range.pop_front()) {
|
for (; !range.empty(); range.pop_front()) {
|
||||||
|
@ -877,6 +1007,13 @@ inline void fill(R range, T const &v) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @brief Fills the given input range with calls to `gen`.
|
||||||
|
*
|
||||||
|
* Iterates over `range` and assigns `gen()` to each element. The elements
|
||||||
|
* must therefore be actual lvalue references so they can be assigned to.
|
||||||
|
*
|
||||||
|
* @see ostd::fill(), ostd::iota()
|
||||||
|
*/
|
||||||
template<typename R, typename F>
|
template<typename R, typename F>
|
||||||
inline void generate(R range, F gen) {
|
inline void generate(R range, F gen) {
|
||||||
for (; !range.empty(); range.pop_front()) {
|
for (; !range.empty(); range.pop_front()) {
|
||||||
|
@ -884,8 +1021,29 @@ inline void generate(R range, F gen) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @brief Swaps the contents of two input ranges.
|
||||||
|
*
|
||||||
|
* Both ranges must meet the conditions of ostd::is_range_element_swappable
|
||||||
|
* and the value types of both ranges must also be the same. The ranges must
|
||||||
|
* be at least ostd::input_range_tag. The swapping stops as soon as any of
|
||||||
|
* the ranges becomes empty.
|
||||||
|
*
|
||||||
|
* @returns The `range1` and `range2` in a pair after swapping is done.
|
||||||
|
*/
|
||||||
template<typename R1, typename R2>
|
template<typename R1, typename R2>
|
||||||
inline std::pair<R1, R2> swap_ranges(R1 range1, R2 range2) {
|
inline std::pair<R1, R2> swap_ranges(R1 range1, R2 range2) {
|
||||||
|
static_assert(
|
||||||
|
is_range_element_swappable<R1>,
|
||||||
|
"The range element accessors must allow swapping"
|
||||||
|
);
|
||||||
|
static_assert(
|
||||||
|
is_range_element_swappable<R2>,
|
||||||
|
"The range element accessors must allow swapping"
|
||||||
|
);
|
||||||
|
static_assert(
|
||||||
|
std::is_same_v<range_value_t<R1>, range_value_t<R2>>,
|
||||||
|
"The range value types must be the same"
|
||||||
|
);
|
||||||
while (!range1.empty() && !range2.empty()) {
|
while (!range1.empty() && !range2.empty()) {
|
||||||
using std::swap;
|
using std::swap;
|
||||||
swap(range1.front(), range2.front());
|
swap(range1.front(), range2.front());
|
||||||
|
@ -895,6 +1053,13 @@ inline std::pair<R1, R2> swap_ranges(R1 range1, R2 range2) {
|
||||||
return std::make_pair(range1, range2);
|
return std::make_pair(range1, range2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @brief Fills the given input range with `value++`.
|
||||||
|
*
|
||||||
|
* Iterates over `range` and assigns `value++` to each element. The elements
|
||||||
|
* must therefore be actual lvalue references so they can be assigned to.
|
||||||
|
*
|
||||||
|
* @see ostd::fill(), ostd::generate()
|
||||||
|
*/
|
||||||
template<typename R, typename T>
|
template<typename R, typename T>
|
||||||
inline void iota(R range, T value) {
|
inline void iota(R range, T value) {
|
||||||
for (; !range.empty(); range.pop_front()) {
|
for (; !range.empty(); range.pop_front()) {
|
||||||
|
@ -902,6 +1067,18 @@ inline void iota(R range, T value) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @brief Left-folds the `range` into `init`.
|
||||||
|
*
|
||||||
|
* The `init` is an initial value. The `range` is iterated and each
|
||||||
|
* element is added to `init` using the `+` operator. Once that is
|
||||||
|
* done, `init` is returned.
|
||||||
|
*
|
||||||
|
* The `range` must be at least ostd::input_range_tag.
|
||||||
|
*
|
||||||
|
* A function-based version as well as right-folds are provided as well.
|
||||||
|
*
|
||||||
|
* @see ostd::foldl_f(), ostd::foldr()
|
||||||
|
*/
|
||||||
template<typename R, typename T>
|
template<typename R, typename T>
|
||||||
inline T foldl(R range, T init) {
|
inline T foldl(R range, T init) {
|
||||||
for (; !range.empty(); range.pop_front()) {
|
for (; !range.empty(); range.pop_front()) {
|
||||||
|
@ -910,6 +1087,18 @@ inline T foldl(R range, T init) {
|
||||||
return init;
|
return init;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @brief Left-folds the `range` into `init`.
|
||||||
|
*
|
||||||
|
* The `init` is an initial value. The `range` is iterated and `init`
|
||||||
|
* is assigned as `init = func(init, range.front())`. Once that is
|
||||||
|
* done, `init` is returned.
|
||||||
|
*
|
||||||
|
* The `range` must be at least ostd::input_range_tag.
|
||||||
|
*
|
||||||
|
* A `+` operator-based version as well as right-folds are provided as well.
|
||||||
|
*
|
||||||
|
* @see ostd::foldl(), ostd::foldr_f()
|
||||||
|
*/
|
||||||
template<typename R, typename T, typename F>
|
template<typename R, typename T, typename F>
|
||||||
inline T foldl_f(R range, T init, F func) {
|
inline T foldl_f(R range, T init, F func) {
|
||||||
for (; !range.empty(); range.pop_front()) {
|
for (; !range.empty(); range.pop_front()) {
|
||||||
|
@ -918,12 +1107,21 @@ inline T foldl_f(R range, T init, F func) {
|
||||||
return init;
|
return init;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @brief A pipeable version of ostd::foldl().
|
||||||
|
*
|
||||||
|
* The `init` is forwarded.
|
||||||
|
*/
|
||||||
template<typename T>
|
template<typename T>
|
||||||
inline auto foldl(T &&init) {
|
inline auto foldl(T &&init) {
|
||||||
return [init = std::forward<T>(init)](auto &obj) mutable {
|
return [init = std::forward<T>(init)](auto &obj) mutable {
|
||||||
return foldl(obj, std::forward<T>(init));
|
return foldl(obj, std::forward<T>(init));
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @brief A pipeable version of ostd::foldl_f().
|
||||||
|
*
|
||||||
|
* The `init` and `func` are forwarded.
|
||||||
|
*/
|
||||||
template<typename T, typename F>
|
template<typename T, typename F>
|
||||||
inline auto foldl_f(T &&init, F &&func) {
|
inline auto foldl_f(T &&init, F &&func) {
|
||||||
return [
|
return [
|
||||||
|
@ -933,6 +1131,18 @@ inline auto foldl_f(T &&init, F &&func) {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @brief Right-folds the `range` into `init`.
|
||||||
|
*
|
||||||
|
* The `init` is an initial value. The `range` is iterated backwards
|
||||||
|
* and each element is added to `init` using the `+` operator. Once
|
||||||
|
* that is done, `init` is returned.
|
||||||
|
*
|
||||||
|
* The `range` must be at least ostd::bidirectional_range_tag.
|
||||||
|
*
|
||||||
|
* A function-based version as well as left-folds are provided as well.
|
||||||
|
*
|
||||||
|
* @see ostd::foldr_f(), ostd::foldl()
|
||||||
|
*/
|
||||||
template<typename R, typename T>
|
template<typename R, typename T>
|
||||||
inline T foldr(R range, T init) {
|
inline T foldr(R range, T init) {
|
||||||
for (; !range.empty(); range.pop_back()) {
|
for (; !range.empty(); range.pop_back()) {
|
||||||
|
@ -941,6 +1151,18 @@ inline T foldr(R range, T init) {
|
||||||
return init;
|
return init;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @brief Right-folds the `range` into `init`.
|
||||||
|
*
|
||||||
|
* The `init` is an initial value. The `range` is iterated backwards
|
||||||
|
* and `init` is assigned as `init = func(init, range.back())`. Once
|
||||||
|
* that is done, `init` is returned.
|
||||||
|
*
|
||||||
|
* The `range` must be at least ostd::bidirectional_range_tag.
|
||||||
|
*
|
||||||
|
* A `+` operator-based version as well as left-folds are provided as well.
|
||||||
|
*
|
||||||
|
* @see ostd::foldr(), ostd::foldl_f()
|
||||||
|
*/
|
||||||
template<typename R, typename T, typename F>
|
template<typename R, typename T, typename F>
|
||||||
inline T foldr_f(R range, T init, F func) {
|
inline T foldr_f(R range, T init, F func) {
|
||||||
for (; !range.empty(); range.pop_back()) {
|
for (; !range.empty(); range.pop_back()) {
|
||||||
|
@ -949,12 +1171,21 @@ inline T foldr_f(R range, T init, F func) {
|
||||||
return init;
|
return init;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @brief A pipeable version of ostd::foldr().
|
||||||
|
*
|
||||||
|
* The `init` is forwarded.
|
||||||
|
*/
|
||||||
template<typename T>
|
template<typename T>
|
||||||
inline auto foldr(T &&init) {
|
inline auto foldr(T &&init) {
|
||||||
return [init = std::forward<T>(init)](auto &obj) mutable {
|
return [init = std::forward<T>(init)](auto &obj) mutable {
|
||||||
return foldr(obj, std::forward<T>(init));
|
return foldr(obj, std::forward<T>(init));
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @brief A pipeable version of ostd::foldr_f().
|
||||||
|
*
|
||||||
|
* The `init` and `func` are forwarded.
|
||||||
|
*/
|
||||||
template<typename T, typename F>
|
template<typename T, typename F>
|
||||||
inline auto foldr_f(T &&init, F &&func) {
|
inline auto foldr_f(T &&init, F &&func) {
|
||||||
return [
|
return [
|
||||||
|
@ -1153,6 +1384,10 @@ inline auto filter(P &&pred) {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @} */
|
||||||
|
|
||||||
} /* namespace ostd */
|
} /* namespace ostd */
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/** @} */
|
||||||
|
|
Loading…
Reference in a new issue