Difference between revisions of "cpp/algorithm/ranges/find end"
(→ Possible implementation: reimplemented as a niebloid) |
Andreas Krug (Talk | contribs) m (fmt) |
||
(17 intermediate revisions by 3 users not shown) | |||
Line 2: | Line 2: | ||
{{cpp/algorithm/ranges/navbar}} | {{cpp/algorithm/ranges/navbar}} | ||
{{dcl begin}} | {{dcl begin}} | ||
− | {{dcl header | algorithm}} | + | {{dcl header|algorithm}} |
{{dcl h|Call signature}} | {{dcl h|Call signature}} | ||
− | {{dcl | num=1 | since=c++20 |1= | + | {{dcl|num=1|since=c++20|1= |
− | template<std:: | + | template< std::forward_iterator I1, std::sentinel_for<I1> S1, |
− | + | std::forward_iterator I2, std::sentinel_for<I2> S2, | |
− | + | class Pred = ranges::equal_to, | |
− | + | class Proj1 = std::identity, | |
− | + | class Proj2 = std::identity > | |
− | + | requires std::indirectly_comparable<I1, I2, Pred, Proj1, Proj2> | |
− | + | constexpr ranges::subrange<I1> | |
− | + | find_end( I1 first1, S1 last1, I2 first2, S2 last2, | |
+ | Pred pred = {}, Proj1 proj1 = {}, Proj2 proj2 = {} ); | ||
}} | }} | ||
− | {{dcl | num=2 | since=c++20 |1= | + | {{dcl|num=2|since=c++20|1= |
− | template< | + | template< ranges::forward_range R1, ranges::forward_range R2, |
− | + | class Pred = ranges::equal_to, | |
− | + | class Proj1 = std::identity, | |
− | + | class Proj2 = std::identity > | |
− | + | requires std::indirectly_comparable<ranges::iterator_t<R1>, | |
− | + | ranges::iterator_t<R2>, | |
− | + | Pred, Proj1, Proj2> | |
− | + | constexpr ranges::borrowed_subrange_t<R1> | |
+ | find_end( R1&& r1, R2&& r2, Pred pred = {}, | ||
+ | Proj1 proj1 = {}, Proj2 proj2 = {} ); | ||
}} | }} | ||
{{dcl end}} | {{dcl end}} | ||
− | @1@ Searches for the | + | @1@ Searches for the ''last'' occurrence of the sequence {{range|first2|last2}} in the range {{range|first1|last1}}, after projection with {{c|proj1}} and {{c|proj2}} respectively. The projected elements are compared using the binary predicate {{c|pred}}. |
− | @2@ Same as {{v|1}}, but uses {{ | + | @2@ Same as {{v|1}}, but uses {{c|r1}} as the first source range and {{c|r2}} as the second source range, as if using {{c|ranges::begin(r1)}} as {{c|first1}}, {{c|ranges::end(r1)}} as {{c|last1}}, {{c|ranges::begin(r2)}} as {{c|first2}}, and {{c|ranges::end(r2)}} as {{c|last2}}. |
{{cpp/ranges/niebloid}} | {{cpp/ranges/niebloid}} | ||
Line 34: | Line 37: | ||
===Parameters=== | ===Parameters=== | ||
{{par begin}} | {{par begin}} | ||
− | {{par | first1, last1 | the range of elements to examine (aka ''haystack'')}} | + | {{par|first1, last1|the range of elements to examine (aka ''haystack'')}} |
− | {{par | first2, last2 | the range of elements to search for (aka ''needle'')}} | + | {{par|first2, last2|the range of elements to search for (aka ''needle'')}} |
− | {{par | r1 | the range of elements to examine (aka ''haystack'')}} | + | {{par|r1|the range of elements to examine (aka ''haystack'')}} |
− | {{par | r2 | the range of elements to search for (aka ''needle'')}} | + | {{par|r2|the range of elements to search for (aka ''needle'')}} |
− | {{par | pred | binary predicate to compare the elements}} | + | {{par|pred|binary predicate to compare the elements}} |
− | {{par | proj1 | projection to apply to the elements in the first range}} | + | {{par|proj1|projection to apply to the elements in the first range}} |
− | {{par | proj2 | projection to apply to the elements in the second range}} | + | {{par|proj2|projection to apply to the elements in the second range}} |
{{par end}} | {{par end}} | ||
===Return value=== | ===Return value=== | ||
− | @1@ {{c|ranges::subrange<I1>}} | + | @1@ {{c|ranges::subrange<I1>{} }} value initialized with expression {{c|1={i, i + (i == last1 ? 0 : ranges::distance(first2, last2))} }} that denotes the last occurrence of the sequence {{range|first2|last2}} in range {{range|first1|last1}} (after projections with {{c|proj1}} and {{c|proj2}}). If {{range|first2|last2}} is empty or if no such sequence is found, the return value is effectively initialized with {{c|{last1, last1}<!---->}}. |
− | @2@ | + | @2@ Same as {{v|1}}, except that the return type is {{c|ranges::borrowed_subrange_t<R1>}}. |
===Complexity=== | ===Complexity=== | ||
− | At most {{ | + | At most {{mathjax-or|\(\scriptsize S\cdot(N-S+1)\)|S·(N-S+1)}} applications of the corresponding predicate and each projection, where {{mathjax-or|\(\scriptsize S\)|S}} is {{c|ranges::distance(first2, last2)}} and {{mathjax-or|\(\scriptsize N\)|N}} is {{c|ranges::distance(first1, last1)}} for {{v|1}}, or {{mathjax-or|\(\scriptsize S\)|S}} is {{c|ranges::distance(r2)}} and {{mathjax-or|\(\scriptsize N\)|N}} is {{c|ranges::distance(r1)}} for {{v|2}}. |
− | === Notes === | + | ===Notes=== |
− | + | An implementation can improve efficiency of the search if the input iterators model {{c|std::bidirectional_iterator}} by searching from the end towards the begin. Modelling the {{c|std::random_access_iterator}} may improve the comparison speed. All this however does not change the theoretical complexity of the worst case. | |
===Possible implementation=== | ===Possible implementation=== | ||
− | {{eq fun| 1= | + | {{eq fun|1= |
− | struct find_end_fn { | + | struct find_end_fn |
− | + | { | |
− | + | template<std::forward_iterator I1, std::sentinel_for<I1> S1, | |
− | + | std::forward_iterator I2, std::sentinel_for<I2> S2, | |
− | + | class Pred = ranges::equal_to, | |
− | + | class Proj1 = std::identity, class Proj2 = std::identity> | |
− | requires std:: | + | requires std::indirectly_comparable<I1, I2, Pred, Proj1, Proj2> |
− | + | constexpr ranges::subrange<I1> | |
operator()(I1 first1, S1 last1, | operator()(I1 first1, S1 last1, | ||
I2 first2, S2 last2, Pred pred = {}, | I2 first2, S2 last2, Pred pred = {}, | ||
Proj1 proj1 = {}, Proj2 proj2 = {}) const | Proj1 proj1 = {}, Proj2 proj2 = {}) const | ||
− | + | { | |
− | + | if (first2 == last2) | |
− | + | { | |
− | + | auto last_it = ranges::next(first1, last1); | |
− | + | return {last_it, last_it}; | |
− | + | } | |
− | + | auto result = ranges::search( | |
+ | std::move(first1), last1, first2, last2, pred, proj1, proj2); | ||
− | + | if (result.empty()) | |
− | + | return result; | |
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | for (;;) | |
− | + | { | |
− | + | auto new_result = ranges::search( | |
− | + | std::next(result.begin()), last1, first2, last2, pred, proj1, proj2); | |
− | + | if (new_result.empty()) | |
− | + | return result; | |
− | + | else | |
− | + | result = std::move(new_result); | |
− | + | } | |
+ | } | ||
+ | |||
+ | template<ranges::forward_range R1, ranges::forward_range R2, | ||
+ | class Pred = ranges::equal_to, | ||
+ | class Proj1 = std::identity, | ||
+ | class Proj2 = std::identity> | ||
+ | requires std::indirectly_comparable<ranges::iterator_t<R1>, | ||
+ | ranges::iterator_t<R2>, | ||
+ | Pred, Proj1, Proj2> | ||
+ | constexpr ranges::borrowed_subrange_t<R1> | ||
+ | operator()(R1&& r1, R2&& r2, Pred pred = {}, | ||
+ | Proj1 proj1 = {}, Proj2 proj2 = {}) const | ||
{ | { | ||
return (*this)(ranges::begin(r1), ranges::end(r1), | return (*this)(ranges::begin(r1), ranges::end(r1), | ||
Line 103: | Line 110: | ||
}; | }; | ||
− | inline constexpr find_end_fn find_end{}; | + | inline constexpr find_end_fn find_end {}; |
}} | }} | ||
===Example=== | ===Example=== | ||
− | {{example|code= | + | {{example |
+ | |code= | ||
#include <algorithm> | #include <algorithm> | ||
#include <array> | #include <array> | ||
#include <cctype> | #include <cctype> | ||
#include <iostream> | #include <iostream> | ||
− | |||
#include <ranges> | #include <ranges> | ||
+ | #include <string_view> | ||
− | void print(std:: | + | void print(const auto haystack, const auto needle) |
− | std::cout << " | + | { |
− | for (const auto c : | + | const auto pos = std::distance(haystack.begin(), needle.begin()); |
− | std::cout << "\" at position " << pos << '\n'; | + | std::cout << "In \""; |
+ | for (const auto c : haystack) | ||
+ | std::cout << c; | ||
+ | std::cout << "\" found \""; | ||
+ | for (const auto c : needle) | ||
+ | std::cout << c; | ||
+ | std::cout << "\" at position [" << pos << ".." << pos + needle.size() << ")\n" | ||
+ | << std::string(4 + pos, ' ') << std::string(needle.size(), '^') << '\n'; | ||
} | } | ||
Line 124: | Line 139: | ||
{ | { | ||
using namespace std::literals; | using namespace std::literals; | ||
− | constexpr auto secret{" | + | constexpr auto secret{"password password word..."sv}; |
− | constexpr auto | + | constexpr auto wanted{"password"sv}; |
− | constexpr std:: | + | |
− | + | constexpr auto found1 = std::ranges::find_end( | |
+ | secret.cbegin(), secret.cend(), wanted.cbegin(), wanted.cend()); | ||
+ | print(secret, found1); | ||
− | constexpr auto | + | constexpr auto found2 = std::ranges::find_end(secret, "word"sv); |
− | + | print(secret, found2); | |
− | print | + | |
− | + | const auto found3 = std::ranges::find_end(secret, "ORD"sv, | |
− | + | [](const char x, const char y) { // uses a binary predicate | |
+ | return std::tolower(x) == std::tolower(y); | ||
+ | }); | ||
+ | print(secret, found3); | ||
− | const auto | + | const auto found4 = std::ranges::find_end(secret, "SWORD"sv, {}, {}, |
− | []( | + | [](char c) { return std::tolower(c); }); // projects the 2nd range |
− | print( | + | print(secret, found4); |
− | + | static_assert(std::ranges::find_end(secret, "PASS"sv).empty()); // => not found | |
− | + | ||
− | + | ||
} | } | ||
− | | output= | + | |output= |
− | + | In "password password word..." found "password" at position [9..17) | |
− | + | ^^^^^^^^ | |
− | + | In "password password word..." found "word" at position [18..22) | |
− | + | ^^^^ | |
+ | In "password password word..." found "ord" at position [19..22) | ||
+ | ^^^ | ||
+ | In "password password word..." found "sword" at position [12..17) | ||
+ | ^^^^^ | ||
}} | }} | ||
===See also=== | ===See also=== | ||
{{dsc begin}} | {{dsc begin}} | ||
− | {{dsc inc | cpp/algorithm/dsc | + | {{dsc inc|cpp/algorithm/ranges/dsc find_last}} |
− | {{dsc inc | cpp/algorithm/ranges/dsc | + | {{dsc inc|cpp/algorithm/ranges/dsc find}} |
− | {{dsc inc | cpp/algorithm/ranges/dsc | + | {{dsc inc|cpp/algorithm/ranges/dsc find_first_of}} |
− | {{dsc inc | cpp/algorithm/ranges/dsc | + | {{dsc inc|cpp/algorithm/ranges/dsc adjacent_find}} |
− | {{dsc inc | cpp/algorithm/ranges/dsc search}} | + | {{dsc inc|cpp/algorithm/ranges/dsc search}} |
− | {{dsc inc | cpp/algorithm/ranges/dsc search_n}} | + | {{dsc inc|cpp/algorithm/ranges/dsc search_n}} |
+ | {{dsc inc|cpp/algorithm/dsc find_end}} | ||
{{dsc end}} | {{dsc end}} | ||
{{langlinks|de|es|fr|it|ja|pt|ru|zh}} | {{langlinks|de|es|fr|it|ja|pt|ru|zh}} |
Latest revision as of 06:21, 22 September 2023
Defined in header <algorithm>
|
||
Call signature |
||
template< std::forward_iterator I1, std::sentinel_for<I1> S1, std::forward_iterator I2, std::sentinel_for<I2> S2, |
(1) | (since C++20) |
template< ranges::forward_range R1, ranges::forward_range R2, class Pred = ranges::equal_to, |
(2) | (since C++20) |
[
first2,
last2)
in the range [
first1,
last1)
, after projection with proj1 and proj2 respectively. The projected elements are compared using the binary predicate pred.The function-like entities described on this page are niebloids, that is:
- Explicit template argument lists cannot be specified when calling any of them.
- None of them are visible to argument-dependent lookup.
- When any of them are found by normal unqualified lookup as the name to the left of the function-call operator, argument-dependent lookup is inhibited.
In practice, they may be implemented as function objects, or with special compiler extensions.
Contents |
[edit] Parameters
first1, last1 | - | the range of elements to examine (aka haystack) |
first2, last2 | - | the range of elements to search for (aka needle) |
r1 | - | the range of elements to examine (aka haystack) |
r2 | - | the range of elements to search for (aka needle) |
pred | - | binary predicate to compare the elements |
proj1 | - | projection to apply to the elements in the first range |
proj2 | - | projection to apply to the elements in the second range |
[edit] Return value
[
first2,
last2)
in range [
first1,
last1)
(after projections with proj1 and proj2). If [
first2,
last2)
is empty or if no such sequence is found, the return value is effectively initialized with {last1, last1}.[edit] Complexity
At most S·(N-S+1) applications of the corresponding predicate and each projection, where S is ranges::distance(first2, last2) and N is ranges::distance(first1, last1) for (1), or S is ranges::distance(r2) and N is ranges::distance(r1) for (2).
[edit] Notes
An implementation can improve efficiency of the search if the input iterators model std::bidirectional_iterator by searching from the end towards the begin. Modelling the std::random_access_iterator may improve the comparison speed. All this however does not change the theoretical complexity of the worst case.
[edit] Possible implementation
struct find_end_fn { template<std::forward_iterator I1, std::sentinel_for<I1> S1, std::forward_iterator I2, std::sentinel_for<I2> S2, class Pred = ranges::equal_to, class Proj1 = std::identity, class Proj2 = std::identity> requires std::indirectly_comparable<I1, I2, Pred, Proj1, Proj2> constexpr ranges::subrange<I1> operator()(I1 first1, S1 last1, I2 first2, S2 last2, Pred pred = {}, Proj1 proj1 = {}, Proj2 proj2 = {}) const { if (first2 == last2) { auto last_it = ranges::next(first1, last1); return {last_it, last_it}; } auto result = ranges::search( std::move(first1), last1, first2, last2, pred, proj1, proj2); if (result.empty()) return result; for (;;) { auto new_result = ranges::search( std::next(result.begin()), last1, first2, last2, pred, proj1, proj2); if (new_result.empty()) return result; else result = std::move(new_result); } } template<ranges::forward_range R1, ranges::forward_range R2, class Pred = ranges::equal_to, class Proj1 = std::identity, class Proj2 = std::identity> requires std::indirectly_comparable<ranges::iterator_t<R1>, ranges::iterator_t<R2>, Pred, Proj1, Proj2> constexpr ranges::borrowed_subrange_t<R1> operator()(R1&& r1, R2&& r2, Pred pred = {}, Proj1 proj1 = {}, Proj2 proj2 = {}) const { return (*this)(ranges::begin(r1), ranges::end(r1), ranges::begin(r2), ranges::end(r2), std::move(pred), std::move(proj1), std::move(proj2)); } }; inline constexpr find_end_fn find_end {}; |
[edit] Example
#include <algorithm> #include <array> #include <cctype> #include <iostream> #include <ranges> #include <string_view> void print(const auto haystack, const auto needle) { const auto pos = std::distance(haystack.begin(), needle.begin()); std::cout << "In \""; for (const auto c : haystack) std::cout << c; std::cout << "\" found \""; for (const auto c : needle) std::cout << c; std::cout << "\" at position [" << pos << ".." << pos + needle.size() << ")\n" << std::string(4 + pos, ' ') << std::string(needle.size(), '^') << '\n'; } int main() { using namespace std::literals; constexpr auto secret{"password password word..."sv}; constexpr auto wanted{"password"sv}; constexpr auto found1 = std::ranges::find_end( secret.cbegin(), secret.cend(), wanted.cbegin(), wanted.cend()); print(secret, found1); constexpr auto found2 = std::ranges::find_end(secret, "word"sv); print(secret, found2); const auto found3 = std::ranges::find_end(secret, "ORD"sv, [](const char x, const char y) { // uses a binary predicate return std::tolower(x) == std::tolower(y); }); print(secret, found3); const auto found4 = std::ranges::find_end(secret, "SWORD"sv, {}, {}, [](char c) { return std::tolower(c); }); // projects the 2nd range print(secret, found4); static_assert(std::ranges::find_end(secret, "PASS"sv).empty()); // => not found }
Output:
In "password password word..." found "password" at position [9..17) ^^^^^^^^ In "password password word..." found "word" at position [18..22) ^^^^ In "password password word..." found "ord" at position [19..22) ^^^ In "password password word..." found "sword" at position [12..17) ^^^^^
[edit] See also
(C++23)(C++23)(C++23) |
finds the last element satisfying specific criteria (niebloid) |
(C++20)(C++20)(C++20) |
finds the first element satisfying specific criteria (niebloid) |
(C++20) |
searches for any one of a set of elements (niebloid) |
(C++20) |
finds the first two adjacent items that are equal (or satisfy a given predicate) (niebloid) |
(C++20) |
searches for the first occurrence of a range of elements (niebloid) |
(C++20) |
searches for the first occurrence of a number consecutive copies of an element in a range (niebloid) |
finds the last sequence of elements in a certain range (function template) |