Skip to content

<batteries/seq/...>: Fast, Ergonomic Sequence Processing

<batteries/seq/...>: Fast, Ergonomic Sequence Processing🔗

The Batteries Seq abstraction builds on top of STL iterator ranges, seeking to offer more readable and maintainable code without sacrificing efficiency.

Intro Example🔗

#include <batteries/seq.hpp>

#include <iostream>
#include <utility>
#include <vector>

int main() {
  std::vector<int> nums = {2, 1, -3, 4, 5, -2};

  int answer = batt::as_seq(nums)

         // Take only non-negative items.
         //
         | batt::seq::filter([](int n) {
             return n >= 0;
           })

         // Calculate a running total plus count.
         //
         | batt::seq::reduce(
             // Initial state ({total, count}):
             //
             std::make_pair(0, 0),

             // Reduce function:
             //
             [](std::pair<int, int> totals, int n) {
               return std::make_pair(totals.first + n, totals.second + 1);
             })

         // Divide total by count to get the final answer.
         //
         | batt::seq::apply([](std::pair<int, int> final_totals) {
             return final_totals.first / final_totals.second;
           });

  std::cout << "The average value of non-negative elements is: " 
            << answer << std::endl;

  return 0;
}

Output:

The average value of non-negative elements is: 3

Seq<T> Concept🔗

Seq<T> is a concept that represents an ordered sequence of objects of type T. The class batt::BoxedSeq is a type-erased container for an object that models this concept. The type requirements/interface of batt::BoxedSeq<T> are:

  • Seq<T> must be copy-constructible, copy-assignable, and publically destructible
  • Seq<T> must have a public type member or type alias/typedef named Item, equivalent to T
  • Given an object seq of type Seq<T>:
    • seq.peek() must return a value of type batt::Optional<T>; if the sequence is empty or at its end, batt::None is returned, otherwise the next item in the sequence is returned
    • seq.next() is the same in terms of returned value, but it additionally has the side effect of consuming the returned item from the sequence

STL Ranges to Sequences🔗

The function batt::as_seq is used to create a Seq from STL containers and ranges in order to access the rest of the Seq API. You can pass a range, a pair of iterators, or a starting iterator and a size to overloads of batt::as_seq to do this.

Composing Sequences🔗

Sequences are composed by applying one or more "operators" to transform the sequence or aggregate its values. A given sequence operator Op() is applied to an input sequence seq by using the expression: seq | Op(). The resulting expression is usually another sequence, but it may be a single value (e.g., count(), collect(), etc.). The set of operators defined by batteries is shown the table below. It is possible for your application code to define additional operators by composing the ones included in Batteries, or by following the conventions used to define the included operators.

Sequence Operator Reference🔗

NOTE: All of the functions below are defined in the namespace batt::seq::, which has been omitted for brevity.

Name Description
all_true() Returns true iff all items in the input sequence are true when evaluated in a boolean context. This operator supports short-circuit; i.e., it stops as soon as it encounters the first false-valued item.

#include <batteries/seq/all_true.hpp>
any_true() Returns true iff any items in the input sequence are true when evaluated in a boolean context. This operator supports short-circuit; i.e., it stops as soon as it encounters the first true-valued item.

#include <batteries/seq/any_true.hpp>
apply(Fn) Applies the passed function to the entire sequence as a single value, returning the result. Not to be confused with map(Fn) which applies a function to each item in the sequence to produce a new sequence.

#include <batteries/seq/apply.hpp>
attach(Data&& data) Produces a sequence identical to the input except that a copy of data is stored in the sequence object. Useful for extending the lifetimes of objects that a sequence may depend on, but doesn't explicitly capture.

#include <batteries/seq/attach.hpp>
boxed() Transforms a templated sequence type to a type-erased container of concrete type batt::BoxedSeq<T>, hiding the details of how the sequence was composed.

#include <batteries/seq/boxed.hpp>
cache_next() Lazily caches a copy of the next item in the input sequence, to speed up repeated calls to peek().

#include <batteries/seq/cache_next.hpp>
chain(seq2) Concatenates seq2 onto the end of the input sequence.

#include <batteries/seq/chain.hpp>
collect(batt::StaticType<T>) Collects all the items in the sequence by repeatedly calling T::emplace_back on a default-constructed instance of container type T.

#include <batteries/seq/collect.hpp>
collect_vec() Collects all the items in the input sequence into a std::vector<T>, where T is the declared Item type of the sequence.

#include <batteries/seq/collect_vec.hpp>
consume() Consumes the entire input sequence, throwing away all values and returning void.

#include <batteries/seq/consume.hpp>
count() Returns the number of items in the sequence, consuming all items in the process.

#include <batteries/seq/count.hpp>
debug_out(out, sep=" ") Prints all items in the input sequence to the passed std::ostream as a side-effect, leaving the input otherwise unmodified.

#include <batteries/seq/print_out.hpp>
decayed() Transforms a sequence comprised of value references into an equivalent sequence of value-copies, similar to std::decay<T>.

#include <batteries/seq/decay.hpp>
deref() Transforms a sequence by dereferencing all items, i.e. item becomes *item. The Item type may be anything which defines operator*, including all built-in pointers, smart pointers, optionals, iterators, etc.

#include <batteries/seq/deref.hpp>
emplace_back(T& container) Invokes container.emplace_back(item) for each item in the input sequence, fully consuming the sequence.

#include <batteries/seq/emplace_back.hpp>
filter(PredFn) Removes any items from the input sequence for which the passed PredFn returns false, producing a filtered sequence.

#include <batteries/seq/filter.hpp>
filter_map(Fn) Combines the effect of map and filter by invoking the passed Fn on each item of the input sequence, producing a temporary sequence of Optional<Item> values, from which all None values are removed.

#include <batteries/seq/filter_map.hpp>
first() Returns the first item in the sequence; equivalent to calling seq.next().

#include <batteries/seq/first.hpp>
flatten() Transforms a sequence of sequences into a flat sequence of the nested items by concatenating all the sub-sequences together.

#include <batteries/seq/flatten.hpp>
for_each(Fn) Invokes the passed function on each item of the sequence, consuming the input. Fn may return type void or value of type batt::seq::LoopControl (i.e., LoopControl::kContinue or LoopControl::kBreak) to control how much of the input sequence to consume, similar to the C++ keywords continue and break within a built-in loop.

#include <batteries/seq/for_each.hpp>
fuse() Transforms a sequence with item type Optional<T> into a sequence of T, terminating the sequence when the first None value is encountered.

#include <batteries/seq/fuse.hpp>
group_by(BinaryPredFn) Transforms a sequence into a sequence of sequences by invoking BinaryPredFn on each pair of adjacent items in the input, creating a split wherever BinaryPredFn returns false.

#include <batteries/seq/group_by.hpp>
inner_reduce(Fn) Given input sequence xs = x0, x1, x2, x3, ..., xN, the expression xs | inner_reduce(fn) produces the return value of the expression fn(...fn(fn(fn(x0, x1), x2, x3)..., xN).

#include <batteries/seq/inner_reduce.hpp>
inspect(Fn) Calls the given Fn on each item of the input sequence, producing a sequence equivalent to the original input. This differs from for_each, which also invokes a function for each input item, but does not produce a sequence.

#include <batteries/seq/inspect.hpp>
inspect_adjacent(BinaryFn) Calls the given BinaryFn on each sequentially adjacent pair of items of the input, producing a sequence equivalent to the original sequence (similar to inspect).

#include <batteries/seq/inspect_adjacent.hpp>
is_sorted() Returns true iff the input sequence is sorted. The item type of the input sequence must support operator<.

#include <batteries/seq/is_sorted.hpp>
is_sorted_by(OrderFn) Returns true iff the input sequence is sorted according to the passed OrderFn, which should behave like the less-than operator.

#include <batteries/seq/is_sorted.hpp>
kmerge() Performs a k-way merge of a sequence of sorted sequences, producing a single sorted sequence. The built in comparison operators (e.g. <, <=, >, etc.) are used to define the ordering of nested items. Behavior is unspecified if the input sequences are not sorted! The input sequences must be copyable, with each copy able to iterate through independent of the others.

#include <batteries/seq/kmerge.hpp>
kmerge_by(OrderFn) Like kmerge(), except the ordering is defined by the passed binary predicate OrderFn, which should behave like the less-than operator (operator<).

#include <batteries/seq/kmerge.hpp>
last() Returns the final item in the sequence if the input is non-empty, else batt::None.

#include <batteries/seq/last.hpp>
map(Fn) Transforms the input sequence by applying the passed Fn to each item in the input to produce the output.

#include <batteries/seq/map.hpp>
map_adjacent(Fn) Transforms x0, x1, x2, x3, ... into fn(x0, x1), fn(x1, x2), fn(x2, x3), ...

#include <batteries/seq/map_adjacent.hpp>
map_fold(state0, fn) Similar to map(Fn: auto (T) -> U), except that the fn takes an additional first argument, the state, and returns a std::tuple<State, U>. This produces both a sequence of output values u0, u1, u2, ... and also a series of state values, state1, state2, state3, ..., with each item's state output passed into the next item's call to fn. This operator is preferable to using map with an Fn that uses mutable state in its implementation.

#include <batteries/seq/map_fold.hpp>
map_pairwise(SeqB, Fn) Given two sequences seqA = a0, a1, a2, ... and seqB = b0, b1, b2, ..., the expression seqA | map_pairwise(seqB, fn) produces a sequence with items fn(a0, b0), fn(a1, b1), fn(a2, b2), ...

#include <batteries/seq/map_pairwise.hpp>
prepend(seq0) Forms a new sequence by concatenating the input sequence onto the end of seq0; this is the opposite of chain.

#include <batteries/seq/prepend.hpp>
print_out(out, sep=" ") Consumes all items in the input stream, printing them to the passed std::ostream out, inserting the passed separator string sep in between each item.

#include <batteries/seq/print_out.hpp>
printable() Turns the input sequence into a value that can be <<-inserted to any std::ostream to print out all items.

#include <batteries/seq/printable.hpp>
product() Returns the arithmetic product of the items in the input sequence. The item type T of the input sequence must support built-in multiplication (operator*), and T{1} must be the multiplicative identity value of T (i.e., "one").

#include <batteries/seq/product.hpp>
reduce(state, fn) Transforms a sequence of items item0, item1, item2, ..., itemN into the result of calling: fn(...fn(fn(fn(state, item0), item1), item2)..., itemN); this operation is sometimes called left-fold or foldl.

#include <batteries/seq/reduce.hpp>
rolling(Fn, init = {}) Like reduce, but instead of evaluating to the single final value, produces a sequence starting with init followed by all intermediate values calculated by Fn (Note: this means rolling produces a sequence one larger than the input sequence). The type of the init param should be the Item type T of the sequence; Fn should have the signature: auto (T, T) -> T.

#include <batteries/seq/rolling.hpp>
rolling_sum(Fn) Produces a sequence that begins with a default-initialized item (i.e. "zero"), followed by a running total of all the items up to that point in the input.

#include <batteries/seq/rolling_sum.hpp>
skip_n(n) Produces a new sequence that doesn't include the first n items of the input sequence. This is the complement of take_n(n).

#include <batteries/seq/skip_n.hpp>
splice(n, InnerSeq) Inserts the items of InnerSeq into the input sequence at position n. This is a more general version of chain and prepend.

#include <batteries/seq/splice.hpp>
status_ok() Transforms a sequence a with item type batt::StatusOr<T> into a sequence b with item type T by unwrapping each input item until one with non-ok status is encountered, at which point b is terminated. If the b ends because of a non-ok item from a, then b.status() may be used to retrieve the error status value.

#include <batteries/seq/status_ok.hpp>
sum() Returns the arithmetic sum of the items in the input sequence. The item type T of the input sequence must support built-in addition (operator+), and T{} must be the additive identity value of T (i.e., "zero").

#include <batteries/seq/sum.hpp>
take_n(n) Produces a new sequence comprised of only the first n items of the input sequence. This is the complement of skip_n(n).

#include <batteries/seq/take_n.hpp>
take_while(PredFn) Produces a sequence with the same items as the input up until the passed PredFn returns false, at which point the sequence is terminated.

#include <batteries/seq/take_while.hpp>
zip(Seqs...) Produces a sequence of tuples constructed from items pulled in unison from all zipped sequences.

#include <batteries/seq/zip.hpp>

Other Functions/Classes🔗

  • Addition
  • Empty<T>
  • lazy
  • NaturalOrder
  • NaturalEqual
  • single_item