std::ranges...tion_result(3)
okay bestie, welcome to the std::ranges...tion_result(3) page from the C++ Standard Libary π (yes, thatβs the spelling on the page, not me, I promise). Buckle up: itβs giving βtiny permutation function, but make it C++20β energy.
β¨ NAME β¨
std::ranges::prev_permutation,std::ranges::prev_permutation_result β and the description on this page is literally the same string again: std::ranges::prev_permutation,std::ranges::prev_permutation_result π. Iconic of it, honestly.
πΈ SYNOPSIS πΈ
Defined in header <algorithm>.
Call signature
(1) since C++20
template< std::bidirectional_iterator I, std::sentinel_for<I> S,
class Comp = ranges::less, class Proj = std::identity >
requires std::sortable<I, Comp, Proj>
constexpr prev_permutation_result<I>
prev_permutation( I first, S last, Comp comp = {}, Proj proj = {} );
(2) since C++20
template< ranges::bidirectional_range R, class Comp = ranges::less,
class Proj = std::identity >
requires std::sortable<ranges::iterator_t<R>, Comp, Proj>
constexpr prev_permutation_result<ranges::borrowed_iterator_t<R>>
prev_permutation( R&& r, Comp comp = {}, Proj proj = {} );
Helper type
(3) since C++20
template< class I >
using prev_permutation_result = ranges::in_found_result<I>;
So, bestie, hereβs the lore:
- Transforms the range
[first, last)into the previous permutation, where the set of all permutations is ordered lexicographically with respect to binary comparison function objectcompand projection function objectproj.
Returns:
{last, true}if the βpreviousβ permutation exists. Otherwise,{last, false}, and transforms the range into the (lexicographically) last permutation, as if by
ranges::sort(first, last, comp, proj);
ranges::reverse(first, last);
- Same as (1), but uses
ras the source range, as if usingranges::begin(r)asfirst, andranges::end(r)aslast.
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.
bestie, the βno explicit template argsβ part is real β donβt try to argue with the niebloid, it will win π
π PARAMETERS π
first, lastβ the range of elements to βpermuteβrβ the range of elements to βpermuteβcompβ comparison function object which returnstrueif the first argument is less than the secondprojβ projection to apply to the elements
π RETURN VALUE π
ranges::prev_permutation_result<I>{last, true}if the new permutation is lexicographically less than the old one.ranges::prev_permutation_result<I>{last, false}if the first permutation was reached and the range was reset to the last permutation.- Same as (1) except that the return type is
ranges::prev_permutation_result<ranges::borrowed_iterator_t<R>>.
π¨ EXCEPTIONS π¨
Any exceptions thrown from iterator operations or the element swap. No cap, thatβs the whole exception section π.
β±οΈ COMPLEXITY β±οΈ
At most \(\scriptsize N/2\)N / 2 swaps, where N is ranges::distance(first, last) in case (1) or ranges::distance(r) in case (2). Averaged over the entire sequence of permutations, typical implementations use about 3 comparisons and 1.5 swaps per call.
π NOTES π
Implementations (e.g. MSVC STL) may enable vectorization when the iterator type models contiguous_iterator and swapping its value type calls neither non-trivial special member function nor ADL-found swap. Thatβs the little optimization tea ββ¨.
π οΈ POSSIBLE IMPLEMENTATION π οΈ
okay this is the whole little function-object situation, no cap:
struct prev_permutation_fn {
template<std::bidirectional_iterator I, std::sentinel_for<I> S,
class Comp = ranges::less, class Proj = std::identity>
requires std::sortable<I, Comp, Proj>
constexpr ranges::prev_permutation_result<I>
operator()(I first, S last, Comp comp = {}, Proj proj = {})
const
{
// check that the sequence has at least two elements
if (first == last)
return {std::move(first), false};
auto i{first};
++i;
if (i == last)
return {std::move(i), false};
auto i_last{ranges::next(first, last)};
i = i_last;
--i;
// main "permutating" loop
for (;;)
{
auto i1{i};
--i;
if (std::invoke(comp, std::invoke(proj, *i1),
std::invoke(proj, *i)))
{
auto j{i_last};
while (!std::invoke(comp, std::invoke(proj, *--j),
std::invoke(proj, *i)))
;
ranges::iter_swap(i, j);
ranges::reverse(i1, last);
return {std::move(i_last), true};
}
// permutation "space" is exhausted
if (i == first)
{
ranges::reverse(first, last);
return {std::move(i_last), false};
}
}
}
template<ranges::bidirectional_range R, class Comp = ranges::less,
class Proj = std::identity>
requires std::sortable<ranges::iterator_t<R>, Comp, Proj>
constexpr
ranges::prev_permutation_result<ranges::borrowed_iterator_t<R>>
operator()(R&& r, Comp comp = {}, Proj proj = {}) const
{
return (*this)(ranges::begin(r), ranges::end(r),
std::move(comp), std::move(proj));
} };
inline constexpr prev_permutation_fn prev_permutation {};
π EXAMPLE π
run this code, bestie:
// Run this code
#include <algorithm>
#include <array>
#include <compare>
#include <functional>
#include <iostream>
#include <string>
struct S
{
char c{};
int i{};
auto operator<=>(const S&) const = default;
friend std::ostream& operator<<(std::ostream& os, const S& s)
{
return os << "{'" << s.c << "', " << s.i << "}";
}
};
auto print = [](auto const& v, char term = ' ')
{
std::cout << "{ ";
for (const auto& e : v)
std::cout << e << ' ';
std::cout << '}' << term;
};
int main()
{
std::cout << "Generate all permutations (iterators case):\n";
std::string s{"cba"};
do print(s);
while (std::ranges::prev_permutation(s.begin(), s.end()).found);
std::cout << "\nGenerate all permutations (range case):\n";
std::array a{'c', 'b', 'a'};
do print(a);
while (std::ranges::prev_permutation(a).found);
std::cout << "\nGenerate all permutations using comparator:\n";
using namespace std::literals;
std::array z{"β"s, "β"s, "β"s};
do print(z);
while (std::ranges::prev_permutation(z, std::greater()).found);
std::cout << "\nGenerate all permutations using projection:\n";
std::array<S, 3> r{S{'C',1}, S{'B',2}, S{'A',3}};
do print(r, '\n');
while (std::ranges::prev_permutation(r, {}, &S::c).found);
}
Output:
Generate all permutations (iterators case):
{ c b a } { c a b } { b c a } { b a c } { a c b } { a b c }
Generate all permutations (range case):
{ c b a } { c a b } { b c a } { b a c } { a c b } { a b c }
Generate all permutations using comparator:
{ β β β } { β β β } { β β β } { β β β } { β β β } { β β β }
Generate all permutations using projection:
{ {'C', 1} {'B', 2} {'A', 3} }
{ {'C', 1} {'A', 3} {'B', 2} }
{ {'B', 2} {'C', 1} {'A', 3} }
{ {'B', 2} {'A', 3} {'C', 1} }
{ {'A', 3} {'C', 1} {'B', 2} }
{ {'A', 3} {'B', 2} {'C', 1} }
π SEE ALSO π
ranges::next_permutationβ generates the next greater lexicographic permutation of a range of elements (C++20) (niebloid)ranges::is_permutationβ determines if a sequence is a permutation of another sequence (C++20) (niebloid)next_permutationβ generates the next greater lexicographic permutation of a range of elements (function template)prev_permutationβ generates the next smaller lexicographic permutation of a range of elements (function template)is_permutationβ determines if a sequence is a permutation of another sequence (C++11) (function template)
mwah π β source/version http://cppreference.com, date 2024.06.10.
π¦ where this page comes from
- nixpkgs unstable: stdman 2024.07.05
- opensuse-leap 16.0: stdman 2024.07.05-bp160.1.9 Β· MIT
- π girlified by deepseek-ai/DeepSeek-V4.1-Flash-1017 on : a language model's rewrite of the original, under its terms