diff --git a/ostd/algorithm.hh b/ostd/algorithm.hh index b445488..bfe345e 100644 --- a/ostd/algorithm.hh +++ b/ostd/algorithm.hh @@ -239,8 +239,8 @@ inline auto sort() { /** @brief Finds the smallest element in the range. * - * It works like std::min_element. The range must be at least - * std::forward_range_tag. The `<` operator is used for comparisons. + * It works like std::min_element(). The range must be at least + * ostd::forward_range_tag. The `<` operator is used for comparisons. * * @see ostd::min_element_cmp(), ostd::max_element() */ @@ -258,7 +258,7 @@ inline R min_element(R range) { /** @brief Finds the smallest element in the range. * * It works like std::min_element. The range must be at least - * std::forward_range_tag. The `compare` function is used for comparisons. + * ostd::forward_range_tag. The `compare` function is used for comparisons. * * @see ostd::min_element(), ostd::max_element_cmp() */ @@ -293,8 +293,8 @@ inline auto min_element_cmp(C &&compare) { /** @brief Finds the largest element in the range. * - * It works like std::max_element. The range must be at least - * std::forward_range_tag. The `<` operator is used for comparisons. + * It works like std::max_element(). The range must be at least + * ostd::forward_range_tag. The `<` operator is used for comparisons. * * @see ostd::max_element_cmp(), ostd::min_element() */ @@ -312,7 +312,7 @@ inline R max_element(R range) { /** @brief Finds the largest element in the range. * * It works like std::max_element. The range must be at least - * std::forward_range_tag. The `compare` function is used for comparisons. + * ostd::forward_range_tag. The `compare` function is used for comparisons. * * @see ostd::max_element(), ostd::min_element_cmp() */ @@ -347,6 +347,13 @@ inline auto max_element_cmp(C &&compare) { /* lexicographical compare */ +/** @brief Like std::lexicographical_compare(), but for ranges. + * + * This version uses the `<` operator for comparisons. This algorithm + * is not multi-pass, so an ostd::input_range_tag is perfectly fine here. + * + * @see ostd::lexicographical_compare_cmp() + */ template inline bool lexicographical_compare(R1 range1, R2 range2) { while (!range1.empty() && !range2.empty()) { @@ -361,6 +368,11 @@ inline bool lexicographical_compare(R1 range1, R2 range2) { } return (range1.empty() && !range2.empty()); } + +/** @brief A pipeable version of ostd::lexicographical_compare(). + * + * The range is forwarded. + */ template inline auto lexicographical_compare(R &&range) { return [range = std::forward(range)](auto &obj) mutable { @@ -368,6 +380,13 @@ inline auto lexicographical_compare(R &&range) { }; } +/** @brief Like std::lexicographical_compare(), but for ranges. + * + * This version uses the `compare` function for comparisons. This algorithm + * is not multi-pass, so an ostd::input_range_tag is perfectly fine here. + * + * @see ostd::lexicographical_compare() + */ template inline bool lexicographical_compare_cmp(R1 range1, R2 range2, C compare) { while (!range1.empty() && !range2.empty()) { @@ -382,6 +401,11 @@ inline bool lexicographical_compare_cmp(R1 range1, R2 range2, C compare) { } return (range1.empty() && !range2.empty()); } + +/** @brief A pipeable version of ostd::lexicographical_compare_cmp(). + * + * The range and comparison function are forwarded. + */ template inline auto lexicographical_compare_cmp(R &&range, C &&compare) { return [ @@ -395,6 +419,13 @@ inline auto lexicographical_compare_cmp(R &&range, C &&compare) { /* algos that don't change the range */ +/** @brief Executes `func` on each element of `range`. + * + * The `func` is called like `func(range.front())`. The algorithm is + * not multi-pass, so an ostd::input_range_tag is perfectly fine. + * + * @returns The `func` by move. + */ template inline F for_each(R range, F func) { for (; !range.empty(); range.pop_front()) { @@ -403,6 +434,10 @@ inline F for_each(R range, F func) { return std::move(func); } +/** @brief A pipeable version of ostd::for_each(). + * + * The function is forwarded. + */ template inline auto for_each(F &&func) { return [func = std::forward(func)](auto &obj) mutable { @@ -410,6 +445,16 @@ inline auto for_each(F &&func) { }; } +/** @brief Checks if every element of `range` matches `pred`. + * + * The `pred` has to return true when called like `pred(range.front())`. + * If it doesn't, `false` is returned. + * + * The predicate is called at most `N` times, where `N` is the range length. + * The range is an ostd::input_range_tag or better. + * + * @see ostd::any_of(), ostd::none_of() + */ template inline bool all_of(R range, P pred) { for (; !range.empty(); range.pop_front()) { @@ -420,6 +465,10 @@ inline bool all_of(R range, P pred) { return true; } +/** @brief A pipeable version of ostd::all_of(). + * + * The function is forwarded. + */ template inline auto all_of(F &&func) { return [func = std::forward(func)](auto &obj) mutable { @@ -427,6 +476,16 @@ inline auto all_of(F &&func) { }; } +/** @brief Checks if any element of `range` matches `pred`. + * + * As soon as `pred` returns true when called like `pred(range.front())`, + * this returns true. Otherwise it returns false. + * + * The predicate is called at most `N` times, where `N` is the range length. + * The range is an ostd::input_range_tag or better. + * + * @see ostd::all_of(), ostd::none_of() + */ template inline bool any_of(R range, P pred) { for (; !range.empty(); range.pop_front()) @@ -434,6 +493,10 @@ inline bool any_of(R range, P pred) { return false; } +/** @brief A pipeable version of ostd::any_of(). + * + * The function is forwarded. + */ template inline auto any_of(F &&func) { return [func = std::forward(func)](auto &obj) mutable { @@ -441,6 +504,16 @@ inline auto any_of(F &&func) { }; } +/** @brief Checks if no element of `range` matches `pred`. + * + * As soon as `pred` returns true when called like `pred(range.front())`, + * this returns false. Otherwise it returns true. + * + * The predicate is called at most `N` times, where `N` is the range length. + * The range is an ostd::input_range_tag or better. + * + * @see ostd::all_of(), ostd::any_of() + */ template inline bool none_of(R range, P pred) { for (; !range.empty(); range.pop_front()) @@ -448,6 +521,10 @@ inline bool none_of(R range, P pred) { return true; } +/** @brief A pipeable version of ostd::none_of(). + * + * The function is forwarded. + */ template inline auto none_of(F &&func) { return [func = std::forward(func)](auto &obj) mutable { @@ -796,7 +873,7 @@ namespace detail { using range_category = std::common_type_t< range_category_t, finite_random_access_range_tag >; - using value_type = R; + using value_type = std::remove_reference_t; using reference = R; using size_type = range_size_t; using difference_type = range_difference_t; @@ -838,6 +915,33 @@ namespace detail { ); } /* namespace detail */ +/** @brief Gets a wrapper range that maps each item by `func`. + * + * The resulting range is at most ostd::finite_random_access_range_tag. + * There are no restrictions on the input range. The resulting range is + * not mutable, it's purely input-type. + * + * The `reference` member type of the range is `R` where `R` is the return + * value of `func`. The `value_type` is `std::remove_reference_t`. The + * size and difference types are preserved. + * + * On each access of a range item (front, back, indexing), the `func` is + * called with the actual wrapped range's item and the result is returned. + * + * An example: + * + * ~~~{.cc} + * auto r = ostd::map(ostd::range(5), [](auto v) { return v + 5; }); + * for (auto i: r) { + * ostd::writeln(i); // 5, 6, 7, 8, 9 + * } + * ~~~ + * + * Because the range is lazy, a new value is computed on each access, but + * the wrapper range also needs no memory of its own. + * + * @see ostd::filter() + */ template inline auto map(R range, F func) { return detail::map_range>( @@ -845,6 +949,10 @@ inline auto map(R range, F func) { ); } +/** @brief A pipeable version of ostd::map(). + * + * The `func` is forwarded. + */ template inline auto map(F &&func) { return [func = std::forward(func)](auto &obj) mutable { @@ -898,15 +1006,53 @@ namespace detail { >, P>; } /* namespace detail */ +/** @brief Gets a wrapper range that filters items by `pred`. + * + * The resulting range is ostd::forward_range_tag at most. The range will + * only contains items for which `pred` returns true. What this means is + * that upon creation, the given range is stored and all iterated until + * an item matching `pred` is reached. On `pop_front()`, this item is + * popped out and the same is done, i.e. again iterated until a new + * item matching `pred` is reached. + * + * In other words, this is done: + * + * ~~~{.cc} + * void advance(R &range, P &pred) { + * while (!range.empty() && !pred()) { + * range.pop_front(); + * } + * } + * + * constructor(R range, P pred) { + * store_range_and_pred(range, pred); + * advance(stored_range, stored_pred); + * } + * + * void pop_front() { + * stored_range.pop_front(); + * advance(stored_range, stored_pred); + * } + * ~~~ + * + * The value, reference, size and difference types are preserved, as are + * calls to `front()` and `empty()`. + * + * @see ostd::map() + */ template inline auto filter(R range, P pred) { return detail::filter_range(range, std::move(pred)); } -template -inline auto filter(F &&func) { - return [func = std::forward(func)](auto &obj) mutable { - return filter(obj, std::forward(func)); +/** @brief A pipeable version of ostd::filter(). + * + * The `pred` is forwarded. + */ +template +inline auto filter(P &&pred) { + return [pred = std::forward

(pred)](auto &obj) mutable { + return filter(obj, std::forward

(pred)); }; }