Adaptors¶
Flux adaptor functions take one or more sequences as input and return a new adapted sequence as output. When iterating, the elements of the adapted sequence are computed lazily from the elements of the underlying sequence. Adaptors are sink functions, meaning they take ownership of the sequences that are passed to them.
While functions below are shown as taking their sequence arguments by value for documentation purposes, in fact they will reject lvalue sequence arguments that are not trivially copyable. If you have a non-trivially copyable sequence then you need to explicitly copy or move it into the adaptor using std::move()
, flux::copy()
or (in C++23) auto(). This is to prevent accidental copying of, for example, large vectors.
You can pass a reference to a sequence into an adaptor using flux::ref()
or flux::mut_ref()
. Doing so introduces a data and lifetime dependency between the original sequence and the returned adaptor object. You must not modify the original sequence object (including calling its destructor) between the creation of the referencing adaptor object and its last use.
adjacent
¶
-
template<distance_t N>
requires (N > 0)
auto adjacent(multipass_sequence auto seq) -> multipass_sequence auto¶ Given a compile-time size
N
and a multipass sequenceseq
, returns a new sequence which yields sliding windows of sizeN
as anN
-tuple of elements ofseq
. If seq has fewer thanN
elements, the adapted sequence will be empty.The
slide()
adaptor is similar toadjacent()
, but takes its window size as a run-time rather than a compile-time parameter, and returns length-n
subsequences rather than tuples.Equivalent to:
zip(seq, drop(seq, 1), drop(seq, 2), ..., drop(seq, N-1));
- Template Parameters:
N – The size of the sliding window. Must be greater than zero.
- Parameters:
seq – A multipass sequence.
- Returns:
A sequence adaptor whose element type is a std::tuple of size
N
, or a std::pair if N == 2.- Models:
Concept
When
Always
seq
is bidirectionalseq
is random-accessNever
seq
is bidirectional and boundedseq
is sizedNever
seq
is read-onlyseq
is const-iterable- Example:
std::vector vec{1, 2, 3, 4, 5}; std::vector<int> sums; // adjacent<3> yields 3-tuples which we can destructure for (auto [a, b, c] : flux::adjacent<3>(std::move(vec))) { sums.push_back(a + b + c); } assert((sums == std::vector{1 + 2 + 3, 2 + 3 + 4, 3 + 4 + 5}));
- See also:
adjacent_filter
¶
-
template<multipass_sequence Seq, typename Pred>
requires std::predicate<Pred&, element_t<Seq>, element_t<Seq>>
auto adjacent_filter(Seq seq, Pred pred) -> multipass_sequence auto;¶ Applies the given binary predicate
pred
to each pair of adjacent elements ofseq
. If the predicate returnsfalse
, the second element of the pair does not appear in the resulting sequence. The first element ofseq
is always included in the output.A common use for
adjacent_filter()
is to remove adjacent equal elements from a sequence, which can be achieved by passing std::not_equal_to{} as the predicate. Thededup()
function is a handy alias for adjacent_filter(not_equal_to{}).- Parameters:
seq – A multipass sequence
pred – A binary predicate to compare sequence elements
- Returns:
The filtered sequence
- Models:
Concept
When
Always
Seq
is bidirectionalNever
Never
Seq
is boundedNever
Never
Seq
is read-onlySeq
is const-iterable- Example:
std::array nums{1, 1, 2, 3, 3, 2, 2}; // The adjacent_filter adaptor applies the given predicate to each pair // of elements in the sequence, and if the predicate returns false then // the second element of the pair is discarded auto filtered1 = flux::adjacent_filter(nums, std::less{}); assert(flux::equal(filtered1, std::array{1, 2, 3})); // For the common case of removing adjacent equal elements, Flux provides // the dedup() function as shorthand for adjacent_filter(std::not_equal_to{}) auto filtered2 = flux::dedup(nums); assert(flux::equal(filtered2, std::array{1, 2, 3, 2})); // We can use adjacent_filter with a custom comparator as well auto compare = [](auto p1, auto p2) { return p1.first != p2.first; }; std::pair<int, int> pairs[] = {{1, 2}, {1, 3}, {1, 4}, {2, 5}, {2, 6}}; auto filtered3 = flux::adjacent_filter(flux::ref(pairs), compare); assert(flux::equal(filtered3, std::array{std::pair{1, 2}, std::pair{2, 5}}));
- See also:
adjacent_map
¶
-
template<distance_t N>
auto adjacent_map(multipass_sequence auto seq, auto func) -> multipass_sequence auto;¶ Applies the
N
-ary functionfunc
to overlapping windows of sizeN
.Equivalent to map(adjacent<N>(seq), unpack(func)), but avoids forming an intermediate tuple.
- Models:
cache_last
¶
-
template<sequence Seq>
requires bounded_sequence<Seq> || (multipass_sequence<Seq> && !infinite_sequence<Seq>)
auto cache_last(Seq seq) -> sequence auto;¶ cache_last()
is used to turn a non-bounded sequence into abounded_sequence
, by internally caching the cursor position of the last element,If passed a sequence that is already bounded, it is returned unchanged. Otherwise, the passed-in sequence must be multipass, and not infinite.
Note that because this adaptor uses internal caching it is not const-iterable.
- Parameters:
seq – A sequence we want to ensure is bounded.
- Returns:
A bounded sequence
- Models:
cartesian_power
¶
-
template<distance_t N>
requires (N >= 0)
auto cartesian_power(multipass_sequence auto seq) -> multipass_sequence auto;¶ Returns a sequence object giving the
N
th Cartesian power of the elements ofseq
. It is equivalent toN
nestedfor
loops each iterating overseq
.The element type of the returned sequence is an
N
-tuple of the elements ofseq
. The number of elements is \(S^N\), where \(S\) is count(seq).Equivalent to cartesian_product(seq, seq, ...), but stores only a single copy of
seq
.- Template Parameters:
N – The cartesian power
- Parameters:
seq – A multipass sequence
- Returns:
A multipass sequence yielding
N
-tuples of all combinations of elements ofseq
- Example:
std::string_view str = "ab"; std::vector<std::string> strings; // cartesian_power<3> gives us 3-tuples which we can destructure // The total number of elements in this case is size(str)^3 = 8 for (auto [x, y, z] : flux::cartesian_power<3>(str)) { strings.push_back(std::string{x, y, z}); } assert((strings == std::vector<std::string>{"aaa", "aab", "aba", "abb", "baa", "bab", "bba", "bbb"}));
- Models:
Concept
When
Always
seq
is bidirectionalseq
is random-accessNever
seq
is boundedseq
is sizedNever
seq
is read-onlyseq
is const-iterable- See also:
cartesian_power_map
¶
-
template<distance_t N>
requires (N >= 0)
auto cartesian_power_map(multipass_sequence auto seq, auto func) -> multipass_sequence auto;¶ Returns a sequence adaptor which applies the
N
-ary functionfunc
to theN
th Cartesian power of the elements ofseq
.The total number of elements in the returned sequence is \(S^N\), where \(S\) is count(seq).
Equivalent to cartesian_power<N>(seq, unpack(func)), but avoids forming an intermediate tuple.
- Template Parameters:
N – The cartesian power
- Parameters:
- Returns:
A multipass sequence whose elements are the result of applying
func
toN
elements ofseq
- Example:
std::array nums{1, 2, 3}; // cartesian_power_map<N> takes a sequence and a callable of N arguments // Here we are using N=2 and the binary function object std::plus auto sums = flux::cartesian_power_map<2>(nums, std::plus{}).to<std::vector<int>>(); assert((sums == std::vector<int>{1 + 1, 1 + 2, 1 + 3, 2 + 1, 2 + 2, 2 + 3, 3 + 1, 3 + 2, 3 + 3}));
- Models:
Concept
When
Always
seq
is bidirectionalseq
is random-accessNever
seq
is boundedseq
is sizedNever
seq
is read-only- See also:
std::views::cartesian_product (C++23)
cartesian_product_map()
cartesian_product
¶
-
auto cartesian_product(sequence auto seq0, multipass_sequence auto... seqs) -> sequence auto;¶
Returns an adaptor yielding the cartesian_product of the input sequences.
The element type of the returned sequence is a tuple of the element types of the input sequences.
- Example:
std::string_view str = "abc"; std::array nums = {1, 2, 3}; // flux::cartesian_product(str, nums) yields all combinations of elements from // the two sequences as a tuple<char, int> auto pairs = flux::cartesian_product(str, nums).to<std::vector>(); using P = std::tuple<char, int>; assert((pairs == std::vector{P{'a', 1}, P{'a', 2}, P{'a', 3}, P{'b', 1}, P{'b', 2}, P{'b', 3}, P{'c', 1}, P{'c', 2}, P{'c', 3}})); // cartesian_product can take an arbitrary number of sequence arguments // The number of elements is the product of the sizes of the input sequences // It is bidirectional and random-access if all the input sequences // satisfy these concepts auto seq = flux::cartesian_product("xy"sv, std::array{1.0, 2.0}, std::vector{111, 222}).reverse(); using T = std::tuple<char, double, int>; assert(flux::equal(seq, std::vector<T>{{'y', 2.0, 222}, {'y', 2.0, 111}, {'y', 1.0, 222}, {'y', 1.0, 111}, {'x', 2.0, 222}, {'x', 2.0, 111}, {'x', 1.0, 222}, {'x', 1.0, 111}}));
- Models:
Concept
When
seq0
is multipassAll passed-in sequences are bidirectional and bounded
All passed-in sequences are random-access and bounded
Never
seq0
is boundedAll passed-in sequences are sized
Never
All passed-in sequences are read-only
All passed-in sequences are const-iterable
- See also:
std::views::cartesian_product (C++23)
cartesian_product_map()
cartesian_product_map
¶
-
template<typename Func, sequence Seq0, multipass_sequence... Seqs>
requires std::regular_invocable<Func&, element_t<Seq0>, element_t<Seqs>...>
auto cartesian_product_with(Func func, Seq0 seq0, Seqs... seqs) -> sequence auto;¶ Given
N
sequences and anN
-ary functionfunc
, appliesfunc
to the cartesian product of the elements of the input sequences.Equivalent to map(cartesian_product(seq0, seqs...), unpack(func)), but avoids forming an intermediate tuple.
- Example:
std::array small = {1, 2}; auto add3 = [](long a, float b, int c) { return double(a) + b + c; }; // Note that because cartesian_product_map takes a variadic number of // sequences, the function argument goes first auto vec = flux::cartesian_product_map(add3, big, medium, small) .to<std::vector>(); assert((vec == std::vector<double>{10101.0, 10102.0, 10201.0, 10202.0, 20101.0, 20102.0, 20201.0, 20202.0}));
- Models:
Concept
When
seq0
is multipassAll passed-in sequences are bidirectional and bounded
All passed-in sequences are random-access and bounded
Never
seq0
is boundedAll passed-in sequences are sized
Never
All passed-in sequences are read-only
All passed-in sequences are const-iterable and
func
is const-invocable- See also:
chain
¶
-
template<sequence Seq0, sequence... Seqs>
requires see_below
auto chain(Seq0 seq0, Seqs... seqs) -> sequence auto;¶ Combines the given input sequences into one logical sequence.
- Requires:
The element types of the input sequences share a common reference type to which they are all convertible. This is the element type of the returned sequence.
The rvalue element types of the input sequences share a common reference type to which they are all convertible. This is the rvalue element type of the returned sequence.
The value types of the input sequences share a common type. This is the value type of the returned sequence.
- Models:
Concept
When
All passed-in sequences are multipass
All passed-in sequences are bidirectional and bounded
All passed-in sequences are random-access and bounded
Never
The last passed-in sequence is bounded
All passed-in sequences are sized
Any passed-in sequence is infinite
All passed-in sequences are read-only
All passed-in sequences are const-iterable
chunk
¶
chunk_by
¶
-
template<multipass_sequence Seq, typename Pred>
requires std::predicate<Pred, element_t<Seq>, element_t<Seq>>
auto chunk_by(Seq seq, Pred pred) -> multipass_sequence auto;¶ Splits
seq
into a sequence of non-overlapping sequences using the binary predicatepred
. The given predicate should returntrue
if both elements should be considered part of the same chunk. If the predicate returnsfalse
, a new chunk will be started, with the second argument topred
belonging to the new chunk.- Models:
cursors
¶
-
auto cursors(multipass_sequence auto seq) -> multipass_sequence auto;¶
Given a sequence
seq
, cursors(seq) returns a new sequence whose elements are the cursors of the original sequence. Thecursors()
sequence retains all the capabilities of the source sequence (bidirectional, random access, sized etc), up tocontiguous_sequence
.This is basically a passthrough adaptor, except that read_at(seq, cur) returns a copy of
cur
.- Parameters:
seq – A multipass sequence
- Returns:
A sequence whose elements are the cursors of
seq
- Models:
Concept
When
Always
seq
is bidirectionalseq
is random-accessNever
seq
is boundedseq
is sizedseq
is infiniteAlways
seq
is const-iterable- Example:
std::array const array{"alpha"sv, "bravo"sv, "charlie"sv, "delta"sv, "echo"sv}; auto long_words = flux::drop(array, 2); // We can use the cursors() adaptor to iterate over the cursors of the // sequence (in this case integer indices) and use those to read from the // original sequence for (auto idx : flux::cursors(long_words)) { std::cout << idx << ": " << long_words[idx] << '\n'; } // prints // 2: charlie // 3: delta // 4: echo
cycle
¶
-
template<sequence Seq>
requires multipass_sequence<Seq> || infinite_sequence<Seq>
auto cycle(Seq seq) -> infinite_sequence auto;¶
-
template<multipass_sequence Seq>
auto cycle(Seq seq, std::integral auto count) -> multipass_sequence auto;¶ Repeats the elements of
seq
endlessly (for the first overload) orcount
times (for the second overload).For the first overload, if
Seq
is already aninfinite_sequence
, it is passed through unchanged.Otherwise, both overloads require a
multipass_sequence
, and the output is always amultipass_sequence
. The adapted sequence is also abidirectional_sequence
whenSeq
is both bidirectional and bounded, and arandom_access_sequence
whenSeq
is random-access and bounded.For the second overload, the returned sequence is additionally always a
bounded_sequence
(even ifSeq
is not), and asized_sequence
when the source sequence is sized.To avoid “spooky action at a distance” (where mutating s[n] would change the value of some other s[m])
cycle()
provides only immutable access to the elements ofseq
: that is, it behaves as ifseq
were first passed throughread_only()
.Caution
In order to provide random-access functionality, cursors for cycled sequences keep a
size_t
count of how many times they have looped round. For very long-running programs using the infinite version ofcycle()
it may be possible to overflow this counter. (Assuming 1000 iterations per second, this would take approximately 49 days on a machine with a 32-bitsize_t
, or around 500 million years on a 64-bit machine.)While this won’t cause undefined behaviour, it’s possible to encounter incorrect results or runtime errors when using the random-access functions on cursors which have overflowed.
- Parameters:
seq – A sequence to cycle through
count – The number of times to loop through the sequence before terminating. If not supplied, the sequence will be repeated endlessly.
- Returns:
An adapted sequence which repeatedly loops through the elements of
seq
.- Models:
Concept
When
Seq
is multipassSeq
is bidirectional and boundedSeq
is random-access and boundedNever
If
count
is suppliedIf
count
is suppliedIf
count
is not suppliedAlways
Seq
is const-iterable- Example:
std::array arr{1, 2, 3}; // cycle(seq) returns an infinite sequence. It's common to use this in // combination with take() to turn it back into a finite sequence: auto cycled1 = flux::take(flux::cycle(arr), 5); assert(flux::equal(cycled1, std::array{1, 2, 3, 1, 2})); // We can also use a cycled sequence as an argument to zip(): std::string_view letters = "ABCDE"; auto zipped = flux::zip(letters, flux::cycle(arr)); using P = std::pair<char const&, int const&>; assert(flux::equal(zipped, std::array{ P{'A', 1}, P{'B', 2}, P{'C', 3}, P{'D', 1}, P{'E', 2} })); // Alternatively, we can provide a second argument to cycle(seq, n) to // get a finite sequence which repeats the source n times: auto cycled2 = flux::cycle(arr, 3); assert(flux::equal(cycled2, std::array{1, 2, 3, 1, 2, 3, 1, 2, 3})); assert(flux::sum(cycled2) == 18); // Note that both versions of cycle() only provide immutable access to their // elements. The following would be a compile error: // flux::fill(cycled2, 99); // ERROR: cannot assign to const reference
- See also:
dedup
¶
-
template<multipass_sequence Seq>
requires std::equality_comparable<element_t<Seq>>
auto dedup(Seq seq) -> multipass_sequence auto;¶ An alias for adjacent_filter(seq, std::ranges::not_equal_to{}). This can be used to remove adjacent elements from a sequence.
- See also:
drop
¶
-
auto drop(sequence auto seq, std::integral auto count) -> sequence auto;¶
Given a sequence
seq
and a non-negative integral valuecount
, returns a new sequence which skips the firstcount
elements ofseq
.The returned sequence has the same capabilities as
seq
. Ifseq
has fewer thancount
elements, the returned sequence is empty.- Parameters:
seq – A sequence.
count – A non-negative integral value indicating the number of elements to be skipped.
- Returns:
A sequence adaptor that yields the remaining elements of
seq
, with the firstcount
elements skipped.- Models:
Concept
When
seq
is multipassseq
is bidirectionalseq
is random-accessseq
is contiguousseq
is boundedseq
is sizedseq
is infiniteseq
is read-onlyseq
is const-iterable- Example:
std::vector vec{1, 2, 3, 4, 5}; auto dropped = flux::drop(std::move(vec), 3); assert(flux::size(dropped) == 2); assert(flux::equal(dropped, std::vector{4, 5}));
- See also:
std::views::drop (C++20)
drop_while
¶
-
template<sequence Seq, typename Pred>
requires std::predicate<Pred&, element_t<Seq>>
auto drop_while(Seq seq, Pred pred) -> sequence auto;¶ Skips elements from the from of
seq
whilepred
returnstrue
. Oncepred
has returnedfalse
, the adaptor has done its job and the remaining elements ofseq
are returned as normal.- Models:
filter
¶
filter_deref
¶
-
template<sequence Seq>
requires optional_like<element_t<Seq>>
auto filter_deref(Seq seq) -> sequence auto;¶ Given a sequence of “optional-like” elements (e.g.
std::optional
,flux::optional
, T* etc…), filters out those elements which return false after conversion to bool, and performs a dereference of the remaining elements.Equivalent to filter_map(seq, std::identity{}).
- Models:
Concept
When
Seq
is multipassSeq
is bidirectionalNever
Never
Seq
is boundedNever
Seq
is infiniteSeq
is read-onlySeq
is const-iterable andFunc
is const-invocable- See also:
filter_map
¶
-
template<sequence Seq, typename Func>
requires std::invocable<Func&, element_t<Seq>> && optional_like<std::invoke_result_t<Func&, element_t<Seq>>>
auto filter_map(Seq seq, Func func) -> sequence auto;¶ Performs both filtering and mapping using a single function. Given a unary function
func
returning an “optional-like” type, the returned adaptor filters out those elements for whichfunc
returns a “disengaged” optional (that is, those which return false after conversion to bool). It then dereferences the remaining elements using operator*. Equivalent to:map(seq, func) .filter([](auto&& arg) { return static_cast<bool>(arg) }) .map([](auto&& arg) -> decltype(auto) { return *std::forward(arg); });
- Models:
flatten
¶
-
template<sequence Seq>
requires sequence<element_t<Seq>>
auto flatten(Seq seq) -> sequence auto;¶ Given a sequence-of-sequences, removes one level of nesting.
- Models:
Concept
When
Seq
is multipass, element_t<Seq> is multipass and element_t<Seq> is a reference typeSeq
is bidirectional, element_t<Seq> is bidirectional and element_t<Seq> is a reference typeNever
Never
Seq
is boundedNever
Never
Seq
is read-onlySeq
and element_t<Seq> are both const-iterable multipass sequences, and element_t<Seq> is a reference type
flatten_with
¶
-
template<sequence Seq>
requires sequence<element_t<Seq>>
auto flatten_with(Seq seq, value_t<element_t<Seq>> value) -> sequence auto;¶
-
template<sequence Seq, multipass_sequence Pattern>
requires see_below
auto flatten_with(Seq seq, Pattern pattern) -> sequence auto;¶ Works like
flatten()
, but insertsvalue
(for the first overload) or all the elements ofpattern
(for the second overload) between the elements ofseq
.- Requires:
For the second overload, let element_t<Seq> be
InnerSeq
. Then the expression in the requires clause is equivalent to:sequence<InnerSeq> && std::common_reference_with<element_t<InnerSeq>, element_t<Pattern>> && std::common_reference_with<rvalue_element_t<InnerSeq>, rvalue_element_t<Pattern>> && std::common_with<value_t<InnerSeq>, value_t<Pattern>>;
- Models:
Concept
When
Seq
is multipass, element_t<Seq> is multipass and element_t<Seq> is a reference typeSeq
is bidirectional, element_t<Seq> is bidirectional and bounded, Pattern is bidirectional and bounded, and element_t<Seq> is a reference typeNever
Never
Seq
is boundedNever
Never
Seq
is read-onlySeq
, element_t<Seq> andPattern
are all const-iterable multipass sequences, and element_t<Seq> is a reference type
map
¶
mask
¶
-
template<sequence Seq, sequence Mask>
requires boolean_testable<element_t<Mask>>
auto mask(Seq seq, Mask where) -> sequence auto;¶ Given a sequence of values and a sequence of booleans,
mask()
yields those elements ofseq
for which the corresponding element ofwhere
evaluates to true. Iteration is complete when either of the two input sequences is exhausted.The returned sequence models the lowest common category of the two input sequences, up to
bidirectional_sequence
. It is also abounded_sequence
and asized_sequence
when both inputs model these concepts.- Parameters:
seq – A sequence of values
where – A sequence whose element type is convertible to bool
- Returns:
An adapted sequence whose elements are the elements of
seq
, filtered by the corresponding elements ofwhere
- Models:
Concept
When
Never
Never
Seq
is read-only- Example:
std::array values{"one"sv, "two"sv, "three"sv, "four"sv, "five"sv}; std::array selectors{true, false, true, false, true}; // flux::mask() selects those elements of values for which the corresponding // element of selectors is true auto masked = flux::mask(values, selectors); assert(flux::equal(masked, std::array{"one"sv, "three"sv, "five"sv})); // Note that the selectors sequence can have any element type which is // explicitly convertible to bool std::array int_selectors{0, 0, 0, 1, 1}; auto masked2 = flux::mask(values, int_selectors); assert(flux::equal(masked2, std::array{"four"sv, "five"sv}));
- See also:
pairwise
¶
-
auto pairwise(multipass_sequence auto seq) -> multipass_sequence auto;¶
Returns an adaptor which yields pairs of elements of
seq
. It is an alias foradjacent<2>()
.- Parameters:
seq – A multipass sequence.
- Returns:
A multipass sequence yielding pairs of elements of
seq
.
pairwise_map
¶
prescan
¶
-
template<sequence Seq, typename Func, std::movable Init>
requires foldable<Seq, Func, Init>
auto prescan(Seq seq, Func func, Init init) -> sequence auto;¶ Returns a stateful sequence adaptor which yields “partial folds” using the binary function
func
.First, this adaptor initialises an internal variable
state
toinit
and yields a read-only reference to this state. Then, for each successive elementelem
of the underlying sequence, it sets:state = func(std::move(state), std::forward(elem));
and yields a read-only reference to the new state.
The final value yielded by this adaptor is the same as fold(seq, func, init).
Because this adaptor needs to maintain internal state, it is only ever single-pass. However it is a
bounded_sequence
when the underlying sequence is bounded and asized_sequence
when the underlying sequence is sized.Unlike
scan()
, this function performs an exclusive scan, that is, the Nth element of the adapted sequence does not include the Nth element of the underlying sequence. The adaptor returned byprescan()
always yields at least one element – the initial value – followed by the elements that would be yielded by thescan()
adaptor.- Parameters:
- Returns:
A sequence adaptor which performs an exclusive scan of the elements of
seq
usingfunc
.- Models:
- Example:
// We can compute the triangular numbers using prescan() std::array const ints{1, 2, 3, 4, 5}; // Note that unlike scan(), the initial value for prescan() is required, and // is the first element of the resulting sequence, which has one more element // than the input auto tri_nums = flux::prescan(ints, std::plus{}, 0); assert(flux::equal(tri_nums, std::array{0, 1, 3, 6, 10, 15}));
- See also:
read_only
¶
-
template<sequence Seq>
auto read_only(Seq seq) -> read_only_sequence auto;¶ Returns an adapted sequence which prevents direct modification of the elements of
seq
. The returned sequence retains the capabilities of the source sequence, all the way up tocontiguous_sequence
.If
Seq
is already aread_only_sequence
, then it is returned unchanged. Otherwise,read_only()
is equivalent to:map(seq, [](auto&& elem) -> const_element_t<Seq> { return static_cast<const_element_t<Seq>>(std::forward(elem)); });
except that the returned sequence will be a
contiguous_sequence
if the source sequence models that concept. In this case, the pointer returned fromdata()
will have type value_t<Seq> const*.- Parameters:
seq – A sequence
- Returns:
An adapted sequence which provides read-only access to the elements of
seq
- Models:
Concept
When
Seq
is multipassSeq
is bidirectionalSeq
is random-accessSeq
is contiguousSeq
is boundedSeq
is sizedSeq
is infiniteAlways
Seq
is const-iterable- Example:
// We can use the read_only_sequence concept to statically require a sequence // whose elements are immutable bool contains_a_two(flux::read_only_sequence auto&& seq) { for (auto&& elem : seq) { if (elem == 2) { // What if we wrote `elem = 2` (assignment) by mistake? return true; } } return false; } int main() { auto seq = flux::filter(std::vector{1, 2, 3, 4, 5}, flux::pred::even); // We cannot pass seq directly, as it yields mutable int& elements // contains_a_two(seq); // COMPILE ERROR // ...but we can use read_only() so that the sequence yields immutable // elements of type int const&. assert(contains_a_two(flux::read_only(std::move(seq)))); }
- See also:
std::views::as_const() (C++23)
reverse
¶
-
template<bidirectional_sequence Seq>
requires bounded_sequence<Seq>
auto reverse(Seq seq) -> bidirectional_sequence auto;¶ Given a bounded, bidirectional sequence
seq
, returns an adaptor which yields the elements ofseq
in reverse order.- Models:
scan
¶
-
template<sequence Seq, typename Func, std::movable Init = value_t<Seq>>
requires foldable<Seq, Func, Init>
auto scan(Seq seq, Func func, Init init = {}) -> sequence auto;¶ Returns a stateful sequence adaptor which yields “partial folds” using the binary function
func
.First, this adaptor initialises an internal variable
state
toinit
. Then, for each successive elementelem
of the underlying sequence, it sets:state = func(std::move(state), std::forward(elem));
and yields a read-only reference to the new state.
The final value yielded by this adaptor is the same as fold(seq, func, init).
Because this adaptor needs to maintain internal state, it is only ever single-pass. However it is a
bounded_sequence
when the underlying sequence is bounded and asized_sequence
when the underlying sequence is sized.Unlike
prescan()
, this function performs an inclusive scan, that is, the Nth element of the adapted sequence includes the Nth element of the underlying sequence. The adapted sequence always yields the same number of elements as the underlying sequence.- Parameters:
seq – A sequence to adapt
func – A binary callable of the form R(R, element_t<Seq>), where
R
is constructible fromInit
init – The initial value for the scan. If not supplied, a default constructed object of type
value_t<Seq>
is used.
- Returns:
A sequence adaptor which performs an inclusive scan of the elements of
seq
usingfunc
.- Models:
- Example:
// We can compute the triangular numbers using scan() std::array const ints{1, 2, 3, 4, 5}; // Note that unlike prescan(), the initial value for scan() may be omitted // (here defaulting to int{}), and the resulting sequence has the same number // of elements as the original auto tri_nums = flux::scan(ints, std::plus{}); assert(flux::equal(tri_nums, std::array{1, 3, 6, 10, 15}));
scan_first
¶
-
template<sequence Seq, typename Func>
requires foldable<Seq, Func, element_t<Seq>>
auto scan_first(Seq seq, Func func) -> sequence auto;¶ Returns a stateful sequence adaptor which yields “partial folds” using the binary function
func
.When iterated over, the returned sequence first initialises an internal variable
state
with the first element of the underlying sequence, and yields a read-only reference to this state. For each subsequent elementelem
, it sets:state = func(std::move(state), std::forward(elem));
and yields a read-only reference to the internal state. If
seq
is empty, the internal state is never initialised and the resulting sequence is also empty. For a non-empty sequence, the final value yielded byscan_first()
is the same as would be obtained from fold_first(seq, func).Because this adaptor needs to maintain internal state, it is only ever single-pass. However it is a
bounded_sequence
when the underlying sequence is bounded and asized_sequence
when the underlying sequence is sized.Like
scan()
, this function performs an inclusive scan, that is, the Nth element of the adapted sequence includes the Nth element of the underlying sequence. The adapted sequence always yields the same number of elements as the underlying sequence. Unlikescan()
, the first element ofscan_first()
is simply the first element of the underlying sequence, and the suppliedfunc
is only applied to subsequent elements (this is equivalent to the differing behaviours offold()
andfold_first()
respectively).- Parameters:
- Returns:
A sequence adaptor which performs an inclusive scan of the elements of
seq
usingfunc
.- Models:
- Example:
// We can compute the triangular numbers using scan_first() std::array const ints{1, 2, 3, 4, 5}; // Note that unlike scan(), scan_first() never takes an initial value, // and instead the first element of the resulting sequence is the same as // the first element of the underlying sequence: the fold operation is only // applied to subsequent elements. auto tri_nums = flux::scan_first(ints, std::plus{}); assert(flux::equal(tri_nums, std::array{1, 3, 6, 10, 15}));
set_difference
¶
-
template<sequence Seq1, sequence Seq2, typename Cmp = std::compare_three_way>
requires weak_ordering_for<Cmp, Seq1> && weak_ordering_for<Cmp, Seq2>
auto set_difference(Seq1 seq1, Seq2 seq2, Cmp cmp = {}) -> sequence auto;¶ Returns a sequence adaptor which yields the set difference of the two input sequences
seq1
andseq2
, ordered by the given comparison functioncmp
.This function assumes that both
seq1
andseq2
are sorted with respect to the comparison functioncmp
. If the input sequences are not sorted, the contents of the resulting sequence is unspecified.When the resulting sequence is iterated, it will output the elements from
seq1
which are not found in theseq2
according tocmp
. If some element is foundm
times inseq1
andn
times inseq2
, then the resulting sequence yields exactly std::max(m - n, 0) elements.- Parameters:
seq1 – The first sorted sequence.
seq2 – The second sorted sequence.
cmp – A binary comparator whose return type is convertible to
std::weak_ordering
. Both sequences must be sorted with respect to this comparator.
- Returns:
A sequence adaptor that yields those elements of seq1 which do not also appear in seq2.
- Models:
Concept
When
Never
Never
Never
Never
Never
Never
Seq1
is read-onlySeq1
andSeq2
are both const-iterable, andcmp
is const-invocable- Example:
std::array arr1{0, 1, 2, 3, 4, 5}; std::array arr2{ 1, 3, 5}; auto merged = flux::set_difference(flux::ref(arr1), flux::ref(arr2)); assert(flux::equal(merged, std::array{0, 2, 4}));
set_intersection
¶
-
template<sequence Seq1, sequence Seq2, typename Cmp = std::compare_three_way>
requires weak_ordering_for<Cmp, Seq1> && weak_ordering_for<Cmp, Seq2>
auto set_intersection(Seq1 seq1, Seq2 seq2, Cmp cmp = {}) -> sequence auto;¶ Returns a sequence adaptor which yields the set intersection of the two input sequences
seq1
andseq2
, ordered by the given comparison functioncmp
.This function assumes that both
seq1
andseq2
are sorted with respect to the comparison functioncmp
. If the input sequences are not sorted, the contents of the resulting sequence is unspecified.When the resulting sequence is iterated, it will output the elements from
seq1
that are found in both sorted sequences according tocmp
. If some element is foundm
times inseq1
andn
times inseq2
, then the resulting sequence yields exactlystd::min(n, m)
elements.- Parameters:
seq1 – The first sorted sequence.
seq2 – The second sorted sequence.
cmp – A binary comparator whose return type is convertible to
std::weak_ordering
. Both sequences must be sorted with respect to this comparator.
- Returns:
A sequence adaptor that represents the set intersection of the two input sequences.
- Models:
Concept
When
Never
Never
Never
Never
Never
Never
Seq1
is read-onlySeq1
andSeq2
are both const-iterable, andcmp
is const-invocable- Example:
std::array arr1{0, 1, 2, 3, 4, 5}; std::array arr2{ 1, 3, 5}; auto merged = flux::set_intersection(flux::ref(arr1), flux::ref(arr2)); assert(flux::equal(merged, std::array{1, 3, 5}));
set_symmetric_difference
¶
-
template<sequence Seq1, sequence Seq2, typename Cmp = std::compare_three_way>
requires see_below
auto set_symmetric_difference(Seq1 seq1, Seq2 seq2, Cmp cmp = {}) -> sequence auto;¶ Returns a sequence adaptor which yields the set symmetric difference of the two input sequences
seq1
andseq2
, ordered by the given comparison functioncmp
.This function assumes that both
seq1
andseq2
are sorted with respect to the comparison functioncmp
. If the input sequences are not sorted, the contents of the resulting sequence is unspecified.When the resulting sequence is iterated, it will output the elements that are found in either of the sequence, but not in both of them according to
cmp
. If some element is foundm
times inseq1
andn
times inseq2
, then the resulting sequence yields exactlystd::abs(m - n)
elements, preserving order:if m > n, the final m - n of these elements from
seq1
if m < n, the final n - m of these elements from
seq2
- Requires:
The expression in the
requires
clause is equivalent to:std::common_reference_with<element_t<Seq1>, element_t<Seq2>> && std::common_reference_with<rvalue_element_t<Seq1>, rvalue_element_t<Seq2>> && requires { typename std::common_type_t<value_t<Seq1>, value_t<Seq2>>; } && weak_ordering_for<Cmp, Seq1> && weak_ordering_for<Cmp, Seq2>
- Parameters:
seq1 – The first sequence to merge.
seq2 – The second sequence to merge.
cmp – A binary comparator whose return type is convertible to
std::weak_ordering
. Both sequences must be sorted with respect to this comparator.
- Returns:
A sequence adaptor that yields elements of seq1 and seq2 which do not appear in both sequences.
- Models:
Concept
When
Never
Never
Never
Never
Never
std::common_reference_t<element_t<Seq1>, element_t<Seq2>> is an object type or a const reference
Seq1
andSeq2
are both const-iterable, andcmp
is const-invocable- Example:
std::array arr1{0, 1, 2, 3, 4, 5}; std::array arr2{ 1, 3, 5, 6, 7}; auto merged = flux::set_symmetric_difference(flux::ref(arr1), flux::ref(arr2)); assert(flux::equal(merged, std::array{0, 2, 4, 6, 7}));
set_union
¶
-
template<sequence Seq1, sequence Seq2, typename Cmp = std::compare_three_way>
requires see_below
auto set_union(Seq1 seq1, Seq2 seq2, Cmp cmp = {}) -> sequence auto;¶ Returns a sequence adaptor which yields the set union of the two input sequences
seq1
andseq2
, ordered by the given comparison functioncmp
.This function assumes that both
seq1
andseq2
are sorted with respect to the comparison functioncmp
. If the input sequences are not sorted, the contents of the resulting sequence is unspecified.When the resulting sequence is iterated, it will output the elements from the two input sequences in order according to
cmp
. If some element is foundm
times inseq1
andn
times inseq2
, then the resulting sequence yields exactlystd::max(n, m)
elements.- Requires:
The expression in the
requires
clause is equivalent to:std::common_reference_with<element_t<Seq1>, element_t<Seq2>> && std::common_reference_with<rvalue_element_t<Seq1>, rvalue_element_t<Seq2>> && requires { typename std::common_type_t<value_t<Seq1>, value_t<Seq2>>; } && weak_ordering_for<Cmp, Seq1> && weak_ordering_for<Cmp, Seq2>
- Parameters:
seq1 – The first sorted sequence to merge.
seq2 – The second sorted sequence to merge.
cmp – A binary comparator whose return type is convertible to
std::weak_ordering
. Both sequences must be sorted with respect to this comparator.
- Returns:
A sequence adaptor that represents the set union of the two input sequences.
- Models:
Concept
When
Never
Never
Never
Never
Never
std::common_reference_t<element_t<Seq1>, element_t<Seq2>> is an object type or a const reference
Seq1
andSeq2
are both const-iterable, andcmp
is const-invocable- Example:
std::array arr1{1, 3, 5}; std::array arr2{1, 2, 4, 6}; auto merged = flux::set_union(flux::ref(arr1), flux::ref(arr2)); assert(flux::equal(merged, std::array{1, 2, 3, 4, 5, 6}));
slide
¶
-
auto slide(multipass_sequence auto seq, std::integral auto win_sz) -> multipass_sequence auto;¶
Returns an adaptor which yields length-
win_sz
overlapping subsequences ofseq
.The
adjacent()
adaptor is similar toslide()
, but takes its window size argument as a compile-time rather than a run-time parameter, and yieldN
-tuples rather than subsequences.- Models:
split
¶
-
template<multipass_sequence Seq, typename Delim>
requires std::equality_comparable_with<element_t<Seq>, Delim const&>
auto split(Seq seq, Delim delim) -> multipass_sequence auto;¶
-
template<multipass_sequence Seq, multipass_sequence Pattern>
requires std::equality_comparable_with<element_t<Seq>, element_t<Pattern>>
auto split(Seq seq, Pattern pattern) -> multipass_sequence auto;¶
-
template<multipass_sequence Seq, typename Pred>
requires std::predicate<Pred const&, element_t<seq>>
auto split(Seq seq, Pred pred) -> multipass_sequence auto;¶ Splits a
multipass_sequence
into a sequence-of-subsequences using the given argument.The first overload takes a delimiter, which must be equality comparable with the source sequence’s value type. The source sequence will be split on each occurrence of the delimiter, with the delimiter itself removed. Consecutive delimiters will result in empty subsequences in the output. If the source sequence begins with a delimiter then the first subsequence will be empty, and likewise if it ends with a delimiter then the final subsequence will be empty.
The second overload takes another sequence, the
pattern
, whose elements must be equality comparable with the elements of the source sequence. The source is split whenever the pattern occurs as a subsequence. Consecutive (non-overlapping) occurrences of the pattern will result in empty sequences in the output. If ends_with(seq, pattern) is true, the final subsequence will be empty.The third overload takes a unary predicate which will be called with successive elements of the source sequence and returns true when a split should occur. The “
true
” element will be removed from the output. If the predicate returnstrue
for two consecutive of the source, then the output will contain an empty subsequence. If the predicate returnstrue`
for the final element of the source, then the final subsequence will be empty.The returned sequence is always a
multipass_sequence
. It is additionally abounded_sequence
whenSeq
is bounded.- Parameters:
seq – A multipass sequence to split.
delim – For the first overload, a delimiter to split on. Must be equality comparable with the element type of
seq
pattern – For the second overload, a multipass sequence to split on. Its element type must be equality comparable with the element type of
seq
.pred – For the third overload, a unary predicate accepting elements of
seq
, returningtrue
when a split should occur.
- Returns:
A multipass sequence whose elements are subsequences of
seq
.- Models:
Concept
When
Always
Never
Never
Never
Seq
is boundedNever
Never
Seq
is read-onlySeq
is const-iterable, andPattern
is const-iterable (for the second overload) orPred
is const-invocable (for the third overload)- Example:
using flux::equal; // We can split a sequence using a single delimiter auto seq1 = flux::split("here are some words"sv, ' '); assert(equal(seq1, std::array{"here"sv, "are"sv, "some"sv, "words"sv})); // Consecutive delimiters will result in empty subsequences in the output auto seq2 = flux::split("some,,,commas"sv, ','); assert(equal(seq2, std::array{"some"sv, ""sv, ""sv, "commas"sv})); // If the sequence ends with a delimiter, the final subsequence will be empty auto seq3 = flux::split("Two. Sentences."sv, '.'); assert(equal(seq3, std::array{"Two"sv, " Sentences"sv, ""sv})); // We can also split a sequence with a pattern auto seq4 = flux::split(std::vector{1, 2, 3, 4, 5}, std::array{2, 3}); assert(equal(seq4, std::vector{std::vector{1}, std::vector{4, 5}})); // Repeated, non-overlapping patterns result in empty subsequences auto seq5 = flux::split("Hello!!!!World"sv, "!!"sv); assert(equal(seq5, std::array{"Hello"sv, ""sv, "World"sv})); // Overlapping patterns are only matched once auto seq6 = flux::split("Hello!!!World"sv, "!!"sv); assert(equal(seq6, std::array{"Hello"sv, "!World"sv})); // If the sequence begins with the pattern, the first subsequence will // be empty... auto seq7 = flux::split("!!Hello"sv, "!!"sv); assert(equal(seq7, std::array{""sv, "Hello"sv})); // ... and likewise if it ends with the pattern auto seq8 = flux::split("Hello!!"sv, "!!"sv); assert(equal(seq8, std::array{"Hello"sv, ""sv})); // Lastly, we can split using a predicate function auto is_digit = [](char c) { return c >= '0' && c <= '9'; }; auto seq9 = flux::split("These1are2some3words"sv, is_digit); assert(equal(seq9, std::array{"These"sv, "are"sv, "some"sv, "words"sv})); // As usual, consecutive "true" elements in the input will produce // empty subsequences in the output auto seq10 = flux::split("A123B"sv, is_digit); assert(equal(seq10, std::array{"A"sv, ""sv, ""sv, "B"sv})); // It can be useful combine splitting with a "not empty" filter auto is_space = [](char c) { return std::isspace(static_cast<unsigned char>(c)); }; auto seq11 = flux::split("Alpha Bravo\t\rCharlie \n"sv, is_space) .filter(std::not_fn(flux::is_empty)); assert(equal(seq11, std::array{"Alpha"sv, "Bravo"sv, "Charlie"sv}));
- See also:
stride
¶
-
auto stride(sequence auto seq, std::integral auto stride_len) -> sequence auto;¶
Returns an adapted sequence which yields the same first element as
seq
and then yields everystride_len
-th element after that.- Models:
take
¶
take_while
¶
-
template<sequence Seq, typename Pred>
requires std::predicate<Pred&, element_t<Seq>>
auto take_while(Seq seq, Pred pred) -> sequence auto;¶ Returns an adapted sequence which yields elements of
seq
whilepred
returnstrue
. Iteration is complete whenpred
returnsfalse
or the underlying sequence is exhausted.- Models:
unchecked
¶
-
auto unchecked(sequence auto seq) -> sequence auto;¶
A passthrough adaptor which disables bounds checking.
It does so by forwarding each call to
read_at()
on the adapted sequence to theread_at_unchecked()
implementation of the underlying sequence.Danger
Using this adaptor can result in undefined behaviour that would otherwise be caught by Flux’s safety checks. Use it with great caution.
- Models:
zip
¶
-
auto zip(sequence auto... seqs) -> sequence auto;¶
Returns a sequence adaptor which iterates over the input sequences in lock-step. Iteration is complete when any of the input sequences is exhausted.
The element type of the returned sequence is a std::tuple of the element types of the input sequences, or a std::pair for two inputs.
Iteration of the resulting adaptor is complete when any of the input sequences is exhausted.
- Models:
Concept
When
All inputs are multipass
All inputs are bidirectional
seq
is random-accessNever
All inputs are bounded
All inputs are sized
All inputs are infinite
All inputs are read-only
All inputs are const-iterable
zip_map
¶
-
template<typename Func, sequence... Seqs>
requires std::regular_invocable<Func&, element_t<Seqs>...>
auto zip_map(Func func, Seqs... seqs) -> sequence auto;¶ Given
N
input sequences and anN
-ary functionfunc
, returns a sequence adaptor which iterates over the input sequences in lock-step and yields the result of invokingfunc
with the sequence elements.Iteration of the resulting adaptor is complete when any of the input sequences is exhausted.
Equivalent to zip(seqs...).map(unpack(func)), but avoids forming an intermediate tuple.
- Models:
Concept
When
All inputs are multipass
All inputs are bidirectional
seq
is random-accessNever
All inputs are bounded
All inputs are sized
All inputs are infinite
All inputs are read-only
All inputs are const-iterable