Commit a1107271 authored by Joe Richey's avatar Joe Richey Committed by Sara Golemon

Actually denote when we have infinite generators

Summary: Before we didn't do anything at all with the ::infinite value for our
generators, now all the sources operators and sinks are properly notated. The
one signifcant change regarding what is allowed concerns 'cycle' which now (when
called with no arguments) always produces an infinite generator. This is fine
for all but the weirdest of generators fed into cycle.

Reviewed By: @ddrcoder

Differential Revision: D2240328
parent 43d53e06
......@@ -37,6 +37,9 @@ struct ArgumentReference
T&& // int -> int&&
>::type> {};
/**
* Group - The output objects from the GroupBy operator
*/
template <class Key, class Value>
class Group : public GenImpl<Value&&, Group<Key, Value>> {
public:
......@@ -82,6 +85,9 @@ class Group : public GenImpl<Value&&, Group<Key, Value>> {
return true;
}
// GroupBy only takes in finite generators, so we only have finite groups
static constexpr bool infinite = false;
private:
Key key_;
mutable VectorType values_;
......@@ -89,6 +95,12 @@ class Group : public GenImpl<Value&&, Group<Key, Value>> {
namespace detail {
// Classes used for the implementation of Sources, Operators, and Sinks
/*
******************************* Sources ***************************************
*/
/*
* ReferencedSource - Generate values from an STL-like container using
* iterators from .begin() until .end(). Value type defaults to the type of
......@@ -101,23 +113,22 @@ namespace detail {
* string& longestName = from(names)
* | maxBy([](string& s) { return s.size() });
*/
template<class Container,
class Value>
class ReferencedSource :
public GenImpl<Value, ReferencedSource<Container, Value>> {
template <class Container, class Value>
class ReferencedSource
: public GenImpl<Value, ReferencedSource<Container, Value>> {
Container* container_;
public:
explicit ReferencedSource(Container* container)
: container_(container) {}
template<class Body>
public:
explicit ReferencedSource(Container* container) : container_(container) {}
template <class Body>
void foreach(Body&& body) const {
for (auto& value : *container_) {
body(std::forward<Value>(value));
}
}
template<class Handler>
template <class Handler>
bool apply(Handler&& handler) const {
for (auto& value : *container_) {
if (!handler(std::forward<Value>(value))) {
......@@ -126,6 +137,9 @@ public:
}
return true;
}
// from takes in a normal stl structure, which are all finite
static constexpr bool infinite = false;
};
/**
......@@ -142,45 +156,44 @@ public:
*
* Though it is also used for the initializer_list specialization of from().
*/
template<class StorageType,
class Container>
class CopiedSource :
public GenImpl<const StorageType&,
CopiedSource<StorageType, Container>> {
static_assert(
!std::is_reference<StorageType>::value, "StorageType must be decayed");
template <class StorageType, class Container>
class CopiedSource
: public GenImpl<const StorageType&, CopiedSource<StorageType, Container>> {
static_assert(!std::is_reference<StorageType>::value,
"StorageType must be decayed");
public:
// Generator objects are often copied during normal construction as they are
// encapsulated by downstream generators. It would be bad if this caused
// a copy of the entire container each time, and since we're only exposing a
// const reference to the value, it's safe to share it between multiple
// generators.
static_assert(
!std::is_reference<Container>::value,
static_assert(!std::is_reference<Container>::value,
"Can't copy into a reference");
std::shared_ptr<const Container> copy_;
public:
public:
typedef Container ContainerType;
template<class SourceContainer>
template <class SourceContainer>
explicit CopiedSource(const SourceContainer& container)
: copy_(new Container(begin(container), end(container))) {}
explicit CopiedSource(Container&& container) :
copy_(new Container(std::move(container))) {}
explicit CopiedSource(Container&& container)
: copy_(new Container(std::move(container))) {}
// To enable re-use of cached results.
CopiedSource(const CopiedSource<StorageType, Container>& source)
: copy_(source.copy_) {}
template<class Body>
template <class Body>
void foreach(Body&& body) const {
for (const auto& value : *copy_) {
body(value);
}
}
template<class Handler>
template <class Handler>
bool apply(Handler&& handler) const {
// The collection may be reused by others, we can't allow it to be changed.
for (const auto& value : *copy_) {
......@@ -190,6 +203,9 @@ public:
}
return true;
}
// from takes in a normal stl structure, which are all finite
static constexpr bool infinite = false;
};
/**
......@@ -203,17 +219,16 @@ public:
*
* Reminder: Be careful not to invalidate iterators when using ranges like this.
*/
template<class Iterator>
template <class Iterator>
class RangeSource : public GenImpl<typename Range<Iterator>::reference,
RangeSource<Iterator>> {
Range<Iterator> range_;
public:
RangeSource() = default;
explicit RangeSource(Range<Iterator> range)
: range_(std::move(range))
{}
explicit RangeSource(Range<Iterator> range) : range_(std::move(range)) {}
template<class Handler>
template <class Handler>
bool apply(Handler&& handler) const {
for (auto& value : range_) {
if (!handler(value)) {
......@@ -223,12 +238,15 @@ class RangeSource : public GenImpl<typename Range<Iterator>::reference,
return true;
}
template<class Body>
template <class Body>
void foreach(Body&& body) const {
for (auto& value : range_) {
body(value);
}
}
// folly::Range only supports finite ranges
static constexpr bool infinite = false;
};
/**
......@@ -242,17 +260,19 @@ class RangeSource : public GenImpl<typename Range<Iterator>::reference,
* auto indexes = range(0, 10);
* auto endless = seq(0); // 0, 1, 2, 3, ...
*/
template<class Value, class SequenceImpl>
template <class Value, class SequenceImpl>
class Sequence : public GenImpl<const Value&, Sequence<Value, SequenceImpl>> {
static_assert(!std::is_reference<Value>::value &&
!std::is_const<Value>::value, "Value mustn't be const or ref.");
!std::is_const<Value>::value,
"Value mustn't be const or ref.");
Value start_;
SequenceImpl impl_;
public:
public:
explicit Sequence(Value start, SequenceImpl impl)
: start_(std::move(start)), impl_(std::move(impl)) { }
: start_(std::move(start)), impl_(std::move(impl)) {}
template<class Handler>
template <class Handler>
bool apply(Handler&& handler) const {
for (Value current = start_; impl_.test(current); impl_.step(current)) {
if (!handler(current)) {
......@@ -262,70 +282,82 @@ public:
return true;
}
template<class Body>
template <class Body>
void foreach(Body&& body) const {
for (Value current = start_; impl_.test(current); impl_.step(current)) {
body(current);
}
}
// Let the implementation say if we are infinite or not
static constexpr bool infinite = SequenceImpl::infinite;
};
/**
* Sequence implementations (range, sequence, infinite, with/without step)
**/
template<class Value>
template <class Value>
class RangeImpl {
Value end_;
public:
explicit RangeImpl(Value end) : end_(std::move(end)) { }
explicit RangeImpl(Value end) : end_(std::move(end)) {}
bool test(const Value& current) const { return current < end_; }
void step(Value& current) const { ++current; }
static constexpr bool infinite = false;
};
template<class Value, class Distance>
template <class Value, class Distance>
class RangeWithStepImpl {
Value end_;
Distance step_;
public:
explicit RangeWithStepImpl(Value end, Distance step)
: end_(std::move(end)), step_(std::move(step)) { }
: end_(std::move(end)), step_(std::move(step)) {}
bool test(const Value& current) const { return current < end_; }
void step(Value& current) const { current += step_; }
static constexpr bool infinite = false;
};
template<class Value>
template <class Value>
class SeqImpl {
Value end_;
public:
explicit SeqImpl(Value end) : end_(std::move(end)) { }
explicit SeqImpl(Value end) : end_(std::move(end)) {}
bool test(const Value& current) const { return current <= end_; }
void step(Value& current) const { ++current; }
static constexpr bool infinite = false;
};
template<class Value, class Distance>
template <class Value, class Distance>
class SeqWithStepImpl {
Value end_;
Distance step_;
public:
explicit SeqWithStepImpl(Value end, Distance step)
: end_(std::move(end)), step_(std::move(step)) { }
: end_(std::move(end)), step_(std::move(step)) {}
bool test(const Value& current) const { return current <= end_; }
void step(Value& current) const { current += step_; }
static constexpr bool infinite = false;
};
template<class Value>
template <class Value>
class InfiniteImpl {
public:
bool test(const Value& current) const { return true; }
void step(Value& current) const { ++current; }
static constexpr bool infinite = true;
};
/**
* GenratorBuilder - Helper for GENERTATOR macro.
**/
template<class Value>
template <class Value>
struct GeneratorBuilder {
template<class Source,
template <class Source,
class Yield = detail::Yield<Value, Source>>
Yield operator+(Source&& source) {
return Yield(std::forward<Source>(source));
......@@ -336,15 +368,14 @@ struct GeneratorBuilder {
* Yield - For producing values from a user-defined generator by way of a
* 'yield' function.
**/
template<class Value, class Source>
template <class Value, class Source>
class Yield : public GenImpl<Value, Yield<Value, Source>> {
Source source_;
public:
explicit Yield(Source source)
: source_(std::move(source)) {
}
explicit Yield(Source source) : source_(std::move(source)) {}
template<class Handler>
template <class Handler>
bool apply(Handler&& handler) const {
struct Break {};
auto body = [&](Value value) {
......@@ -360,13 +391,13 @@ class Yield : public GenImpl<Value, Yield<Value, Source>> {
}
}
template<class Body>
template <class Body>
void foreach(Body&& body) const {
source_(std::forward<Body>(body));
}
};
template<class Value>
template <class Value>
class Empty : public GenImpl<Value, Empty<Value>> {
public:
template <class Handler>
......@@ -376,6 +407,9 @@ class Empty : public GenImpl<Value, Empty<Value>> {
template <class Body>
void foreach(Body&&) const {}
// No values, so finite
static constexpr bool infinite = false;
};
template <class Value>
......@@ -383,6 +417,7 @@ class SingleReference : public GenImpl<Value&, SingleReference<Value>> {
static_assert(!std::is_reference<Value>::value,
"SingleReference requires non-ref types");
Value* ptr_;
public:
explicit SingleReference(Value& ref) : ptr_(&ref) {}
......@@ -395,6 +430,9 @@ class SingleReference : public GenImpl<Value&, SingleReference<Value>> {
void foreach(Body&& body) const {
body(*ptr_);
}
// One value, so finite
static constexpr bool infinite = false;
};
template <class Value>
......@@ -402,6 +440,7 @@ class SingleCopy : public GenImpl<const Value&, SingleCopy<Value>> {
static_assert(!std::is_reference<Value>::value,
"SingleCopy requires non-ref types");
Value value_;
public:
explicit SingleCopy(Value value) : value_(std::forward<Value>(value)) {}
......@@ -414,10 +453,13 @@ class SingleCopy : public GenImpl<const Value&, SingleCopy<Value>> {
void foreach(Body&& body) const {
body(value_);
}
// One value, so finite
static constexpr bool infinite = false;
};
/*
* Operators
***************************** Operators ***************************************
*/
/**
......@@ -428,37 +470,34 @@ class SingleCopy : public GenImpl<const Value&, SingleCopy<Value>> {
*
* auto squares = seq(1, 10) | map(square) | asVector;
*/
template<class Predicate>
template <class Predicate>
class Map : public Operator<Map<Predicate>> {
Predicate pred_;
public:
Map() = default;
explicit Map(Predicate pred)
: pred_(std::move(pred))
{ }
explicit Map(Predicate pred) : pred_(std::move(pred)) {}
template<class Value,
template <class Value,
class Source,
class Result = typename ArgumentReference<
typename std::result_of<Predicate(Value)>::type
>::type>
class Generator :
public GenImpl<Result, Generator<Value, Source, Result>> {
typename std::result_of<Predicate(Value)>::type>::type>
class Generator : public GenImpl<Result, Generator<Value, Source, Result>> {
Source source_;
Predicate pred_;
public:
explicit Generator(Source source, const Predicate& pred)
: source_(std::move(source)), pred_(pred) {}
template<class Body>
template <class Body>
void foreach(Body&& body) const {
source_.foreach([&](Value value) {
body(pred_(std::forward<Value>(value)));
});
source_.foreach(
[&](Value value) { body(pred_(std::forward<Value>(value))); });
}
template<class Handler>
template <class Handler>
bool apply(Handler&& handler) const {
return source_.apply([&](Value value) {
return handler(pred_(std::forward<Value>(value)));
......@@ -468,14 +507,14 @@ class Map : public Operator<Map<Predicate>> {
static constexpr bool infinite = Source::infinite;
};
template<class Source,
template <class Source,
class Value,
class Gen = Generator<Value, Source>>
Gen compose(GenImpl<Value, Source>&& source) const {
return Gen(std::move(source.self()), pred_);
}
template<class Source,
template <class Source,
class Value,
class Gen = Generator<Value, Source>>
Gen compose(const GenImpl<Value, Source>& source) const {
......@@ -500,25 +539,24 @@ class Map : public Operator<Map<Predicate>> {
*
* will give a vector of all the pointers != nullptr.
*/
template<class Predicate>
template <class Predicate>
class Filter : public Operator<Filter<Predicate>> {
Predicate pred_;
public:
Filter() = default;
explicit Filter(Predicate pred)
: pred_(std::move(pred))
{ }
explicit Filter(Predicate pred) : pred_(std::move(pred)) {}
template<class Value,
class Source>
template <class Value, class Source>
class Generator : public GenImpl<Value, Generator<Value, Source>> {
Source source_;
Predicate pred_;
public:
explicit Generator(Source source, const Predicate& pred)
: source_(std::move(source)), pred_(pred) {}
template<class Body>
template <class Body>
void foreach(Body&& body) const {
source_.foreach([&](Value value) {
if (pred_(std::forward<Value>(value))) {
......@@ -527,7 +565,7 @@ class Filter : public Operator<Filter<Predicate>> {
});
}
template<class Handler>
template <class Handler>
bool apply(Handler&& handler) const {
return source_.apply([&](Value value) -> bool {
if (pred_(std::forward<Value>(value))) {
......@@ -540,14 +578,14 @@ class Filter : public Operator<Filter<Predicate>> {
static constexpr bool infinite = Source::infinite;
};
template<class Source,
template <class Source,
class Value,
class Gen = Generator<Value, Source>>
Gen compose(GenImpl<Value, Source>&& source) const {
return Gen(std::move(source.self()), pred_);
}
template<class Source,
template <class Source,
class Value,
class Gen = Generator<Value, Source>>
Gen compose(const GenImpl<Value, Source>& source) const {
......@@ -564,25 +602,24 @@ class Filter : public Operator<Filter<Predicate>> {
* | until([](Item& item) { return item.score > 100; })
* | asVector;
*/
template<class Predicate>
template <class Predicate>
class Until : public Operator<Until<Predicate>> {
Predicate pred_;
public:
Until() = default;
explicit Until(Predicate pred)
: pred_(std::move(pred))
{}
explicit Until(Predicate pred) : pred_(std::move(pred)) {}
template<class Value,
class Source>
template <class Value, class Source>
class Generator : public GenImpl<Value, Generator<Value, Source>> {
Source source_;
Predicate pred_;
public:
explicit Generator(Source source, const Predicate& pred)
: source_(std::move(source)), pred_(pred) {}
template<class Handler>
template <class Handler>
bool apply(Handler&& handler) const {
bool cancelled = false;
source_.apply([&](Value value) -> bool {
......@@ -597,24 +634,24 @@ class Until : public Operator<Until<Predicate>> {
});
return !cancelled;
}
// Theoretically an 'until' might stop an infinite
static constexpr bool infinite = false;
};
template<class Source,
template <class Source,
class Value,
class Gen = Generator<Value, Source>>
Gen compose(GenImpl<Value, Source>&& source) const {
return Gen(std::move(source.self()), pred_);
}
template<class Source,
template <class Source,
class Value,
class Gen = Generator<Value, Source>>
Gen compose(const GenImpl<Value, Source>& source) const {
return Gen(source.self(), pred_);
}
// Theoretically an 'until' might stop an infinite
static constexpr bool infinite = false;
};
/**
......@@ -628,23 +665,24 @@ class Until : public Operator<Until<Predicate>> {
*/
class Take : public Operator<Take> {
size_t count_;
public:
explicit Take(size_t count)
: count_(count) {}
explicit Take(size_t count) : count_(count) {}
template<class Value,
class Source>
class Generator :
public GenImpl<Value, Generator<Value, Source>> {
template <class Value, class Source>
class Generator : public GenImpl<Value, Generator<Value, Source>> {
Source source_;
size_t count_;
public:
explicit Generator(Source source, size_t count)
: source_(std::move(source)) , count_(count) {}
: source_(std::move(source)), count_(count) {}
template<class Handler>
template <class Handler>
bool apply(Handler&& handler) const {
if (count_ == 0) { return false; }
if (count_ == 0) {
return false;
}
size_t n = count_;
bool cancelled = false;
source_.apply([&](Value value) -> bool {
......@@ -656,16 +694,19 @@ class Take : public Operator<Take> {
});
return !cancelled;
}
// take will stop an infinite generator
static constexpr bool infinite = false;
};
template<class Source,
template <class Source,
class Value,
class Gen = Generator<Value, Source>>
Gen compose(GenImpl<Value, Source>&& source) const {
return Gen(std::move(source.self()), count_);
}
template<class Source,
template <class Source,
class Value,
class Gen = Generator<Value, Source>>
Gen compose(const GenImpl<Value, Source>& source) const {
......@@ -695,14 +736,15 @@ class Stride : public Operator<Stride> {
class Generator : public GenImpl<Value, Generator<Value, Source>> {
Source source_;
size_t stride_;
public:
Generator(Source source, size_t stride)
explicit Generator(Source source, size_t stride)
: source_(std::move(source)), stride_(stride) {}
template <class Handler>
bool apply(Handler&& handler) const {
size_t distance = stride_;
return source_.apply([&](Value value)->bool {
return source_.apply([&](Value value) -> bool {
if (++distance >= stride_) {
if (!handler(std::forward<Value>(value))) {
return false;
......@@ -723,14 +765,21 @@ class Stride : public Operator<Stride> {
}
});
}
// Taking every Nth of an infinite list is still infinte
static constexpr bool infinite = Source::infinite;
};
template <class Source, class Value, class Gen = Generator<Value, Source>>
template <class Source,
class Value,
class Gen = Generator<Value, Source>>
Gen compose(GenImpl<Value, Source>&& source) const {
return Gen(std::move(source.self()), stride_);
}
template <class Source, class Value, class Gen = Generator<Value, Source>>
template <class Source,
class Value,
class Gen = Generator<Value, Source>>
Gen compose(const GenImpl<Value, Source>& source) const {
return Gen(source.self(), stride_);
}
......@@ -740,20 +789,21 @@ class Stride : public Operator<Stride> {
* Sample - For taking a random sample of N elements from a sequence
* (without replacement).
*/
template<class Random>
template <class Random>
class Sample : public Operator<Sample<Random>> {
size_t count_;
Random rng_;
public:
explicit Sample(size_t count, Random rng)
: count_(count), rng_(std::move(rng)) {}
template<class Value,
template <class Value,
class Source,
class Rand,
class StorageType = typename std::decay<Value>::type>
class Generator :
public GenImpl<StorageType&&,
class Generator
: public GenImpl<StorageType&&,
Generator<Value, Source, Rand, StorageType>> {
static_assert(!Source::infinite, "Cannot sample infinite source!");
// It's too easy to bite ourselves if random generator is only 16-bit
......@@ -762,13 +812,16 @@ class Sample : public Operator<Sample<Random>> {
Source source_;
size_t count_;
mutable Rand rng_;
public:
explicit Generator(Source source, size_t count, Random rng)
: source_(std::move(source)) , count_(count), rng_(std::move(rng)) {}
: source_(std::move(source)), count_(count), rng_(std::move(rng)) {}
template<class Handler>
template <class Handler>
bool apply(Handler&& handler) const {
if (count_ == 0) { return false; }
if (count_ == 0) {
return false;
}
std::vector<StorageType> v;
v.reserve(count_);
// use reservoir sampling to give each source value an equal chance
......@@ -790,23 +843,26 @@ class Sample : public Operator<Sample<Random>> {
});
// output is unsorted!
for (auto& val: v) {
for (auto& val : v) {
if (!handler(std::move(val))) {
return false;
}
}
return true;
}
// Only takes N elements, so finite
static constexpr bool infinite = false;
};
template<class Source,
template <class Source,
class Value,
class Gen = Generator<Value, Source, Random>>
Gen compose(GenImpl<Value, Source>&& source) const {
return Gen(std::move(source.self()), count_, rng_);
}
template<class Source,
template <class Source,
class Value,
class Gen = Generator<Value, Source, Random>>
Gen compose(const GenImpl<Value, Source>& source) const {
......@@ -825,21 +881,20 @@ class Sample : public Operator<Sample<Random>> {
*/
class Skip : public Operator<Skip> {
size_t count_;
public:
explicit Skip(size_t count)
: count_(count) {}
explicit Skip(size_t count) : count_(count) {}
template<class Value,
class Source>
class Generator :
public GenImpl<Value, Generator<Value, Source>> {
template <class Value, class Source>
class Generator : public GenImpl<Value, Generator<Value, Source>> {
Source source_;
size_t count_;
public:
explicit Generator(Source source, size_t count)
: source_(std::move(source)) , count_(count) {}
: source_(std::move(source)), count_(count) {}
template<class Body>
template <class Body>
void foreach(Body&& body) const {
if (count_ == 0) {
source_.foreach(body);
......@@ -855,7 +910,7 @@ class Skip : public Operator<Skip> {
});
}
template<class Handler>
template <class Handler>
bool apply(Handler&& handler) const {
if (count_ == 0) {
return source_.apply(std::forward<Handler>(handler));
......@@ -870,17 +925,18 @@ class Skip : public Operator<Skip> {
});
}
// Skipping N items of an infinite source is still infinite
static constexpr bool infinite = Source::infinite;
};
template<class Source,
template <class Source,
class Value,
class Gen = Generator<Value, Source>>
Gen compose(GenImpl<Value, Source>&& source) const {
return Gen(std::move(source.self()), count_);
}
template<class Source,
template <class Source,
class Value,
class Gen = Generator<Value, Source>>
Gen compose(const GenImpl<Value, Source>& source) const {
......@@ -901,29 +957,25 @@ class Skip : public Operator<Skip> {
* })
* | take(10);
*/
template<class Selector, class Comparer>
template <class Selector, class Comparer>
class Order : public Operator<Order<Selector, Comparer>> {
Selector selector_;
Comparer comparer_;
public:
Order() = default;
explicit Order(Selector selector)
: selector_(std::move(selector))
{}
explicit Order(Selector selector) : selector_(std::move(selector)) {}
Order(Selector selector,
Comparer comparer)
: selector_(std::move(selector))
, comparer_(std::move(comparer))
{}
Order(Selector selector, Comparer comparer)
: selector_(std::move(selector)), comparer_(std::move(comparer)) {}
template<class Value,
template <class Value,
class Source,
class StorageType = typename std::decay<Value>::type,
class Result = typename std::result_of<Selector(Value)>::type>
class Generator :
public GenImpl<StorageType&&,
class Generator
: public GenImpl<StorageType&&,
Generator<Value, Source, StorageType, Result>> {
static_assert(!Source::infinite, "Cannot sort infinite source!");
Source source_;
......@@ -940,10 +992,9 @@ class Order : public Operator<Order<Selector, Comparer>> {
std::sort(vals.begin(), vals.end(), comparer);
return std::move(vals);
}
public:
Generator(Source source,
Selector selector,
Comparer comparer)
Generator(Source source, Selector selector, Comparer comparer)
: source_(std::move(source)),
selector_(std::move(selector)),
comparer_(std::move(comparer)) {}
......@@ -956,14 +1007,14 @@ class Order : public Operator<Order<Selector, Comparer>> {
return asVector();
}
template<class Body>
template <class Body>
void foreach(Body&& body) const {
for (auto& value : asVector()) {
body(std::move(value));
}
}
template<class Handler>
template <class Handler>
bool apply(Handler&& handler) const {
auto comparer = [&](const StorageType& a, const StorageType& b) {
// swapped for minHeap
......@@ -980,16 +1031,19 @@ class Order : public Operator<Order<Selector, Comparer>> {
}
return true;
}
// Can only be run on and produce finite generators
static constexpr bool infinite = false;
};
template<class Source,
template <class Source,
class Value,
class Gen = Generator<Value, Source>>
Gen compose(GenImpl<Value, Source>&& source) const {
return Gen(std::move(source.self()), selector_, comparer_);
}
template<class Source,
template <class Source,
class Value,
class Gen = Generator<Value, Source>>
Gen compose(const GenImpl<Value, Source>& source) const {
......@@ -1057,14 +1111,21 @@ class GroupBy : public Operator<GroupBy<Selector>> {
}
return true;
}
// Can only be run on and produce finite generators
static constexpr bool infinite = false;
};
template <class Source, class Value, class Gen = Generator<Value, Source>>
template <class Source,
class Value,
class Gen = Generator<Value, Source>>
Gen compose(GenImpl<Value, Source>&& source) const {
return Gen(std::move(source.self()), selector_);
}
template <class Source, class Value, class Gen = Generator<Value, Source>>
template <class Source,
class Value,
class Gen = Generator<Value, Source>>
Gen compose(const GenImpl<Value, Source>& source) const {
return Gen(source.self(), selector_);
}
......@@ -1079,17 +1140,17 @@ class GroupBy : public Operator<GroupBy<Selector>> {
* auto c = from(vector) | assert_type<int&>() | sum;
*
*/
template<class Expected>
template <class Expected>
class TypeAssertion : public Operator<TypeAssertion<Expected>> {
public:
template<class Source, class Value>
template <class Source, class Value>
const Source& compose(const GenImpl<Value, Source>& source) const {
static_assert(std::is_same<Expected, Value>::value,
"assert_type() check failed");
return source.self();
}
template<class Source, class Value>
template <class Source, class Value>
Source&& compose(GenImpl<Value, Source>&& source) const {
static_assert(std::is_same<Expected, Value>::value,
"assert_type() check failed");
......@@ -1109,18 +1170,16 @@ class TypeAssertion : public Operator<TypeAssertion<Expected>> {
* })
* | take(10);
*/
template<class Selector>
template <class Selector>
class Distinct : public Operator<Distinct<Selector>> {
Selector selector_;
public:
Distinct() = default;
explicit Distinct(Selector selector)
: selector_(std::move(selector))
{}
explicit Distinct(Selector selector) : selector_(std::move(selector)) {}
template<class Value,
class Source>
template <class Value, class Source>
class Generator : public GenImpl<Value, Generator<Value, Source>> {
Source source_;
Selector selector_;
......@@ -1135,12 +1194,10 @@ class Distinct : public Operator<Distinct<Selector>> {
typedef typename std::decay<KeyType>::type KeyStorageType;
public:
Generator(Source source,
Selector selector)
: source_(std::move(source)),
selector_(std::move(selector)) {}
Generator(Source source, Selector selector)
: source_(std::move(source)), selector_(std::move(selector)) {}
template<class Body>
template <class Body>
void foreach(Body&& body) const {
std::unordered_set<KeyStorageType> keysSeen;
source_.foreach([&](Value value) {
......@@ -1150,7 +1207,7 @@ class Distinct : public Operator<Distinct<Selector>> {
});
}
template<class Handler>
template <class Handler>
bool apply(Handler&& handler) const {
std::unordered_set<KeyStorageType> keysSeen;
return source_.apply([&](Value value) -> bool {
......@@ -1160,16 +1217,20 @@ class Distinct : public Operator<Distinct<Selector>> {
return true;
});
}
// While running distinct on an infinite sequence might produce a
// conceptually finite sequence, it will take infinite time
static constexpr bool infinite = Source::infinite;
};
template<class Source,
template <class Source,
class Value,
class Gen = Generator<Value, Source>>
Gen compose(GenImpl<Value, Source>&& source) const {
return Gen(std::move(source.self()), selector_);
}
template<class Source,
template <class Source,
class Value,
class Gen = Generator<Value, Source>>
Gen compose(const GenImpl<Value, Source>& source) const {
......@@ -1181,16 +1242,16 @@ class Distinct : public Operator<Distinct<Selector>> {
* Composer - Helper class for adapting pipelines into functors. Primarily used
* for 'mapOp'.
*/
template<class Operators>
template <class Operators>
class Composer {
Operators op_;
public:
explicit Composer(Operators op)
: op_(std::move(op)) {}
explicit Composer(Operators op) : op_(std::move(op)) {}
template<class Source,
class Ret = decltype(std::declval<Operators>()
.compose(std::declval<Source>()))>
template <class Source,
class Ret = decltype(
std::declval<Operators>().compose(std::declval<Source>()))>
Ret operator()(Source&& source) const {
return op_.compose(std::forward<Source>(source));
}
......@@ -1211,29 +1272,29 @@ class Composer {
*/
class Batch : public Operator<Batch> {
size_t batchSize_;
public:
explicit Batch(size_t batchSize)
: batchSize_(batchSize) {
explicit Batch(size_t batchSize) : batchSize_(batchSize) {
if (batchSize_ == 0) {
throw std::invalid_argument("Batch size must be non-zero!");
}
}
template<class Value,
template <class Value,
class Source,
class StorageType = typename std::decay<Value>::type,
class VectorType = std::vector<StorageType>>
class Generator :
public GenImpl<VectorType&,
class Generator
: public GenImpl<VectorType&,
Generator<Value, Source, StorageType, VectorType>> {
Source source_;
size_t batchSize_;
public:
explicit Generator(Source source, size_t batchSize)
: source_(std::move(source))
, batchSize_(batchSize) {}
: source_(std::move(source)), batchSize_(batchSize) {}
template<class Handler>
template <class Handler>
bool apply(Handler&& handler) const {
VectorType batch_;
batch_.reserve(batchSize_);
......@@ -1255,571 +1316,342 @@ class Batch : public Operator<Batch> {
return shouldContinue;
}
// Taking n-tuples of an infinite source is still infinite
static constexpr bool infinite = Source::infinite;
};
template<class Source,
template <class Source,
class Value,
class Gen = Generator<Value, Source>>
Gen compose(GenImpl<Value, Source>&& source) const {
return Gen(std::move(source.self()), batchSize_);
}
template<class Source,
template <class Source,
class Value,
class Gen = Generator<Value, Source>>
Gen compose(const GenImpl<Value, Source>& source) const {
return Gen(source.self(), batchSize_);
}
};
/*
* Sinks
*/
/**
* FoldLeft - Left-associative functional fold. For producing an aggregate value
* from a seed and a folder function. Useful for custom aggregators on a
* sequence.
* Concat - For flattening generators of generators.
*
* This type is primarily used through the 'foldl' helper method, like:
* This type is usually used through the 'concat' static value, like:
*
* double movingAverage = from(values)
* | foldl(0.0, [](double avg, double sample) {
* return sample * 0.1 + avg * 0.9;
* auto edges =
* from(nodes)
* | map([](Node& x) {
* return from(x.neighbors)
* | map([&](Node& y) {
* return Edge(x, y);
* });
* })
* | concat
* | as<std::set>();
*/
template<class Seed,
class Fold>
class FoldLeft : public Operator<FoldLeft<Seed, Fold>> {
Seed seed_;
Fold fold_;
class Concat : public Operator<Concat> {
public:
FoldLeft() = default;
FoldLeft(Seed seed,
Fold fold)
: seed_(std::move(seed))
, fold_(std::move(fold))
{}
template<class Source,
class Value>
Seed compose(const GenImpl<Value, Source>& source) const {
static_assert(!Source::infinite, "Cannot foldl infinite source");
Seed accum = seed_;
source | [&](Value v) {
accum = fold_(std::move(accum), std::forward<Value>(v));
};
return accum;
}
};
Concat() = default;
template <class Inner,
class Source,
class InnerValue = typename std::decay<Inner>::type::ValueType>
class Generator
: public GenImpl<InnerValue, Generator<Inner, Source, InnerValue>> {
Source source_;
/**
* First - For finding the first value in a sequence.
*
* This type is primarily used through the 'first' static value, like:
*
* int firstThreeDigitPrime = seq(100) | filter(isPrime) | first;
*/
class First : public Operator<First> {
public:
First() = default;
explicit Generator(Source source) : source_(std::move(source)) {}
template<class Source,
class Value,
class StorageType = typename std::decay<Value>::type>
StorageType compose(const GenImpl<Value, Source>& source) const {
Optional<StorageType> accum;
source | [&](Value v) -> bool {
accum = std::forward<Value>(v);
return false;
template <class Handler>
bool apply(Handler&& handler) const {
return source_.apply([&](Inner inner) -> bool {
return inner.apply(std::forward<Handler>(handler));
});
}
template <class Body>
void foreach(Body&& body) const {
source_.foreach([&](Inner inner) {
inner.foreach(std::forward<Body>(body));
});
}
// Resulting concatination is only finite if both Source and Inner are also
// finite. In one sence, if dosn't make sence to call concat when the Inner
// generator is infinite (you could just call first), so we could also just
// static_assert if the inner is infinite. Taking the less restrictive
// approch for now.
static constexpr bool infinite =
Source::infinite || std::decay<Inner>::type::infinite;
};
if (!accum.hasValue()) {
throw EmptySequence();
template <class Value,
class Source,
class Gen = Generator<Value, Source>>
Gen compose(GenImpl<Value, Source>&& source) const {
return Gen(std::move(source.self()));
}
return std::move(accum.value());
template <class Value,
class Source,
class Gen = Generator<Value, Source>>
Gen compose(const GenImpl<Value, Source>& source) const {
return Gen(source.self());
}
};
/**
* IsEmpty - a helper class for isEmpty and notEmpty
* RangeConcat - For flattening generators of iterables.
*
* Essentially returns 'result' if the source is empty. Note that this cannot be
* called on an infinite source, because then there is only one possible return
* value.
* This type is usually used through the 'rconcat' static value, like:
*
* map<int, vector<int>> adjacency;
* auto sinks =
* from(adjacency)
* | get<1>()
* | rconcat()
* | as<std::set>();
*/
template <bool emptyResult>
class IsEmpty : public Operator<IsEmpty<emptyResult>> {
class RangeConcat : public Operator<RangeConcat> {
public:
IsEmpty() = default;
RangeConcat() = default;
template<class Source,
class Value>
bool compose(const GenImpl<Value, Source>& source) const {
static_assert(!Source::infinite,
"Cannot call 'all', 'any', 'isEmpty', or 'notEmpty' on "
"infinite source. 'all' and 'isEmpty' will either return "
"false or hang. 'any' or 'notEmpty' will either return true "
"or hang.");
bool ans = emptyResult;
source | [&](Value v) -> bool {
ans = !emptyResult;
return false;
};
return ans;
}
};
/**
* Reduce - Functional reduce, for recursively combining values from a source
* using a reducer function until there is only one item left. Useful for
* combining values when an empty sequence doesn't make sense.
*
* This type is primarily used through the 'reduce' helper method, like:
*
* sring longest = from(names)
* | reduce([](string&& best, string& current) {
* return best.size() >= current.size() ? best : current;
* });
*/
template<class Reducer>
class Reduce : public Operator<Reduce<Reducer>> {
Reducer reducer_;
public:
Reduce() = default;
explicit Reduce(Reducer reducer)
: reducer_(std::move(reducer))
{}
template<class Source,
class Value,
class StorageType = typename std::decay<Value>::type>
StorageType compose(const GenImpl<Value, Source>& source) const {
Optional<StorageType> accum;
source | [&](Value v) {
if (accum.hasValue()) {
accum = reducer_(std::move(accum.value()), std::forward<Value>(v));
} else {
accum = std::forward<Value>(v);
}
};
if (!accum.hasValue()) {
throw EmptySequence();
}
return accum.value();
}
};
/**
* Count - for simply counting the items in a collection.
*
* This type is usually used through its singleton, 'count':
*
* auto shortPrimes = seq(1, 100) | filter(isPrime) | count;
*/
class Count : public Operator<Count> {
public:
Count() = default;
template<class Source,
class Value>
size_t compose(const GenImpl<Value, Source>& source) const {
static_assert(!Source::infinite, "Cannot count infinite source");
return foldl(size_t(0),
[](size_t accum, Value v) {
return accum + 1;
}).compose(source);
}
};
template <class Range,
class Source,
class InnerValue = typename ValueTypeOfRange<Range>::RefType>
class Generator
: public GenImpl<InnerValue, Generator<Range, Source, InnerValue>> {
Source source_;
/**
* Sum - For simply summing up all the values from a source.
*
* This type is usually used through its singleton, 'sum':
*
* auto gaussSum = seq(1, 100) | sum;
*/
class Sum : public Operator<Sum> {
public:
Sum() = default;
explicit Generator(Source source) : source_(std::move(source)) {}
template<class Source,
class Value,
class StorageType = typename std::decay<Value>::type>
StorageType compose(const GenImpl<Value, Source>& source) const {
static_assert(!Source::infinite, "Cannot sum infinite source");
return foldl(StorageType(0),
[](StorageType&& accum, Value v) {
return std::move(accum) + std::forward<Value>(v);
}).compose(source);
template <class Body>
void foreach(Body&& body) const {
source_.foreach([&](Range range) {
for (auto& value : range) {
body(value);
}
};
/**
* Contains - For testing whether a value matching the given value is contained
* in a sequence.
*
* This type should be used through the 'contains' helper method, like:
*
* bool contained = seq(1, 10) | map(square) | contains(49);
*/
template<class Needle>
class Contains : public Operator<Contains<Needle>> {
Needle needle_;
public:
explicit Contains(Needle needle)
: needle_(std::move(needle))
{}
template<class Source,
class Value,
class StorageType = typename std::decay<Value>::type>
bool compose(const GenImpl<Value, Source>& source) const {
static_assert(!Source::infinite,
"Calling contains on an infinite source might cause "
"an infinite loop.");
return !(source | [this](Value value) {
return !(needle_ == std::forward<Value>(value));
});
}
};
/**
* Min - For a value which minimizes a key, where the key is determined by a
* given selector, and compared by given comparer.
*
* This type is usually used through the singletone 'min' or through the helper
* functions 'minBy' and 'maxBy'.
*
* auto oldest = from(people)
* | minBy([](Person& p) {
* return p.dateOfBirth;
* });
*/
template<class Selector,
class Comparer>
class Min : public Operator<Min<Selector, Comparer>> {
Selector selector_;
Comparer comparer_;
public:
Min() = default;
explicit Min(Selector selector)
: selector_(std::move(selector))
{}
Min(Selector selector,
Comparer comparer)
: selector_(std::move(selector))
, comparer_(std::move(comparer))
{}
template<class Value,
class Source,
class StorageType = typename std::decay<Value>::type,
class Key = typename std::decay<
typename std::result_of<Selector(Value)>::type
>::type>
StorageType compose(const GenImpl<Value, Source>& source) const {
Optional<StorageType> min;
Optional<Key> minKey;
source | [&](Value v) {
Key key = selector_(std::forward<Value>(v));
if (!minKey.hasValue() || comparer_(key, minKey.value())) {
minKey = key;
min = std::forward<Value>(v);
template <class Handler>
bool apply(Handler&& handler) const {
return source_.apply([&](Range range) -> bool {
for (auto& value : range) {
if (!handler(value)) {
return false;
}
};
if (!min.hasValue()) {
throw EmptySequence();
}
return min.value();
return true;
});
}
};
/**
* Append - For collecting values from a source into a given output container
* by appending.
*
* This type is usually used through the helper function 'appendTo', like:
*
* vector<int64_t> ids;
* from(results) | map([](Person& p) { return p.id })
* | appendTo(ids);
*/
template<class Collection>
class Append : public Operator<Append<Collection>> {
Collection* collection_;
public:
explicit Append(Collection* collection)
: collection_(collection)
{}
template<class Value,
class Source>
Collection& compose(const GenImpl<Value, Source>& source) const {
source | [&](Value v) {
collection_->insert(collection_->end(), std::forward<Value>(v));
// This is similar to concat, except that the inner iterables all are finite
// so the only thing that matters is that the source is infinite.
static constexpr bool infinite = Source::infinite;
};
return *collection_;
}
};
/**
* Collect - For collecting values from a source in a collection of the desired
* type.
*
* This type is usually used through the helper function 'as', like:
*
* std::string upper = from(stringPiece)
* | map(&toupper)
* | as<std::string>();
*/
template<class Collection>
class Collect : public Operator<Collect<Collection>> {
public:
Collect() = default;
template<class Value,
template <class Value,
class Source,
class StorageType = typename std::decay<Value>::type>
Collection compose(const GenImpl<Value, Source>& source) const {
Collection collection;
source | [&](Value v) {
collection.insert(collection.end(), std::forward<Value>(v));
};
return collection;
class Gen = Generator<Value, Source>>
Gen compose(GenImpl<Value, Source>&& source) const {
return Gen(std::move(source.self()));
}
};
/**
* CollectTemplate - For collecting values from a source in a collection
* constructed using the specified template type. Given the type of values
* produced by the given generator, the collection type will be:
* Container<Value, Allocator<Value>>
*
* The allocator defaults to std::allocator, so this may be used for the STL
* containers by simply using operators like 'as<set>', 'as<deque>',
* 'as<vector>'. 'as', here is the helper method which is the usual means of
* consturcting this operator.
*
* Example:
*
* set<string> uniqueNames = from(names) | as<set>();
*/
template<template<class, class> class Container,
template<class> class Allocator>
class CollectTemplate : public Operator<CollectTemplate<Container, Allocator>> {
public:
CollectTemplate() = default;
template<class Value,
template <class Value,
class Source,
class StorageType = typename std::decay<Value>::type,
class Collection = Container<StorageType, Allocator<StorageType>>>
Collection compose(const GenImpl<Value, Source>& source) const {
Collection collection;
source | [&](Value v) {
collection.insert(collection.end(), std::forward<Value>(v));
};
return collection;
class Gen = Generator<Value, Source>>
Gen compose(const GenImpl<Value, Source>& source) const {
return Gen(source.self());
}
};
/**
* Concat - For flattening generators of generators.
* GuardImpl - For handling exceptions from downstream computation. Requires the
* type of exception to catch, and handler function to invoke in the event of
* the exception. Note that the handler may:
* 1) return true to continue processing the sequence
* 2) return false to end the sequence immediately
* 3) throw, to pass the exception to the next catch
* The handler must match the signature 'bool(Exception&, Value)'.
*
* This type is usually used through the 'concat' static value, like:
* This type is used through the `guard` helper, like so:
*
* auto edges =
* from(nodes)
* | map([](Node& x) {
* return from(x.neighbors)
* | map([&](Node& y) {
* return Edge(x, y);
* });
* auto indexes
* = byLine(STDIN_FILENO)
* | guard<std::runtime_error>([](std::runtime_error& e,
* StringPiece sp) {
* LOG(ERROR) << sp << ": " << e.str();
* return true; // continue processing subsequent lines
* })
* | concat
* | as<std::set>();
*/
class Concat : public Operator<Concat> {
* | eachTo<int>()
* | as<vector>();
*
* TODO(tjackson): Rename this back to Guard.
**/
template <class Exception, class ErrorHandler>
class GuardImpl : public Operator<GuardImpl<Exception, ErrorHandler>> {
ErrorHandler handler_;
public:
Concat() = default;
explicit GuardImpl(ErrorHandler handler) : handler_(std::move(handler)) {}
template<class Inner,
class Source,
class InnerValue = typename std::decay<Inner>::type::ValueType>
class Generator :
public GenImpl<InnerValue, Generator<Inner, Source, InnerValue>> {
template <class Value, class Source>
class Generator : public GenImpl<Value, Generator<Value, Source>> {
Source source_;
ErrorHandler handler_;
public:
explicit Generator(Source source)
: source_(std::move(source)) {}
explicit Generator(Source source, ErrorHandler handler)
: source_(std::move(source)), handler_(std::move(handler)) {}
template<class Handler>
template <class Handler>
bool apply(Handler&& handler) const {
return source_.apply([&](Inner inner) -> bool {
return inner.apply(std::forward<Handler>(handler));
});
return source_.apply([&](Value value) -> bool {
try {
handler(std::forward<Value>(value));
return true;
} catch (Exception& e) {
return handler_(e, std::forward<Value>(value));
}
template<class Body>
void foreach(Body&& body) const {
source_.foreach([&](Inner inner) {
inner.foreach(std::forward<Body>(body));
});
}
// Just passes value though, length unaffected
static constexpr bool infinite = Source::infinite;
};
template<class Value,
template <class Value,
class Source,
class Gen = Generator<Value, Source>>
Gen compose(GenImpl<Value, Source>&& source) const {
return Gen(std::move(source.self()));
return Gen(std::move(source.self()), handler_);
}
template<class Value,
template <class Value,
class Source,
class Gen = Generator<Value, Source>>
Gen compose(const GenImpl<Value, Source>& source) const {
return Gen(source.self());
return Gen(source.self(), handler_);
}
};
/**
* RangeConcat - For flattening generators of iterables.
* Dereference - For dereferencing a sequence of pointers while filtering out
* null pointers.
*
* This type is usually used through the 'rconcat' static value, like:
* This type is usually used through the 'dereference' static value, like:
*
* map<int, vector<int>> adjacency;
* auto sinks =
* from(adjacency)
* | get<1>()
* | rconcat()
* | as<std::set>();
* auto refs = from(ptrs) | dereference;
*/
class RangeConcat : public Operator<RangeConcat> {
class Dereference : public Operator<Dereference> {
public:
RangeConcat() = default;
Dereference() = default;
template<class Range,
template <class Value,
class Source,
class InnerValue = typename ValueTypeOfRange<Range>::RefType>
class Generator
: public GenImpl<InnerValue, Generator<Range, Source, InnerValue>> {
class Result = decltype(*std::declval<Value>())>
class Generator : public GenImpl<Result, Generator<Value, Source, Result>> {
Source source_;
public:
explicit Generator(Source source)
: source_(std::move(source)) {}
explicit Generator(Source source) : source_(std::move(source)) {}
template<class Body>
template <class Body>
void foreach(Body&& body) const {
source_.foreach([&](Range range) {
for (auto& value : range) {
body(value);
source_.foreach([&](Value value) {
if (value) {
return body(*value);
}
});
}
template<class Handler>
template <class Handler>
bool apply(Handler&& handler) const {
return source_.apply([&](Range range) -> bool {
for (auto& value : range) {
if (!handler(value)) {
return false;
}
return source_.apply([&](Value value) -> bool {
if (value) {
return handler(*value);
}
return true;
});
}
// Just passes value though, length unaffected
static constexpr bool infinite = Source::infinite;
};
template<class Value,
class Source,
template <class Source,
class Value,
class Gen = Generator<Value, Source>>
Gen compose(GenImpl<Value, Source>&& source) const {
return Gen(std::move(source.self()));
}
template<class Value,
class Source,
template <class Source,
class Value,
class Gen = Generator<Value, Source>>
Gen compose(const GenImpl<Value, Source>& source) const {
return Gen(source.self());
}
};
/**
* GuardImpl - For handling exceptions from downstream computation. Requires the
* type of exception to catch, and handler function to invoke in the event of
* the exception. Note that the handler may:
* 1) return true to continue processing the sequence
* 2) return false to end the sequence immediately
* 3) throw, to pass the exception to the next catch
* The handler must match the signature 'bool(Exception&, Value)'.
*
* This type is used through the `guard` helper, like so:
* Indirect - For producing a sequence of the addresses of the values in the
* input.
*
* auto indexes
* = byLine(STDIN_FILENO)
* | guard<std::runtime_error>([](std::runtime_error& e,
* StringPiece sp) {
* LOG(ERROR) << sp << ": " << e.str();
* return true; // continue processing subsequent lines
* })
* | eachTo<int>()
* | as<vector>();
* This type is usually used through the 'indirect' static value, like:
*
* TODO(tjackson): Rename this back to Guard.
**/
template<class Exception,
class ErrorHandler>
class GuardImpl : public Operator<GuardImpl<Exception, ErrorHandler>> {
ErrorHandler handler_;
* auto ptrs = from(refs) | indirect;
*/
class Indirect : public Operator<Indirect> {
public:
explicit GuardImpl(ErrorHandler handler) : handler_(std::move(handler)) {}
Indirect() = default;
template<class Value,
class Source>
class Generator : public GenImpl<Value, Generator<Value, Source>> {
template <class Value,
class Source,
class Result = typename std::remove_reference<Value>::type*>
class Generator : public GenImpl<Result, Generator<Value, Source, Result>> {
Source source_;
ErrorHandler handler_;
static_assert(!std::is_rvalue_reference<Value>::value,
"Cannot use indirect on an rvalue");
public:
explicit Generator(Source source,
ErrorHandler handler)
: source_(std::move(source)),
handler_(std::move(handler)) {}
explicit Generator(Source source) : source_(std::move(source)) {}
template <class Body>
void foreach(Body&& body) const {
source_.foreach([&](Value value) {
return body(&value);
});
}
template<class Handler>
template <class Handler>
bool apply(Handler&& handler) const {
return source_.apply([&](Value value) -> bool {
try {
handler(std::forward<Value>(value));
return true;
} catch (Exception& e) {
return handler_(e, std::forward<Value>(value));
}
return handler(&value);
});
}
// Just passes value though, length unaffected
static constexpr bool infinite = Source::infinite;
};
template<class Value,
class Source,
template <class Source,
class Value,
class Gen = Generator<Value, Source>>
Gen compose(GenImpl<Value, Source>&& source) const {
return Gen(std::move(source.self()), handler_);
return Gen(std::move(source.self()));
}
template<class Value,
class Source,
template <class Source,
class Value,
class Gen = Generator<Value, Source>>
Gen compose(const GenImpl<Value, Source>& source) const {
return Gen(source.self(), handler_);
return Gen(source.self());
}
};
......@@ -1843,24 +1675,22 @@ class Cycle : public Operator<Cycle<forever>> {
public:
Cycle() = default;
explicit Cycle(off_t limit)
: limit_(limit) {
explicit Cycle(off_t limit) : limit_(limit) {
static_assert(
!forever,
"Cycle limit consturctor should not be used when forever == true.");
}
template<class Value,
class Source>
template <class Value, class Source>
class Generator : public GenImpl<Value, Generator<Value, Source>> {
Source source_;
off_t limit_;
public:
explicit Generator(Source source, off_t limit)
: source_(std::move(source))
, limit_(limit) {}
: source_(std::move(source)), limit_(limit) {}
template<class Handler>
template <class Handler>
bool apply(Handler&& handler) const {
bool cont;
auto handler2 = [&](Value value) {
......@@ -1875,145 +1705,391 @@ class Cycle : public Operator<Cycle<forever>> {
return false;
}
}
return true;
return true;
}
// This is the hardest one to infer. If we are simply doing a finite cycle,
// then (gen | cycle(n)) is infinite if and only if gen is infinite.
// However, if we are doing an infinite cycle, (gen | cycle) is infinite
// unless gen is empty. However, we will always mark (gen | cycle) as
// infinite, because patterns such as (gen | cycle | count) can either take
// on exactly one value, or infinite loop.
static constexpr bool infinite = forever || Source::infinite;
};
template <class Source,
class Value,
class Gen = Generator<Value, Source>>
Gen compose(GenImpl<Value, Source>&& source) const {
return Gen(std::move(source.self()), limit_);
}
template <class Source,
class Value,
class Gen = Generator<Value, Source>>
Gen compose(const GenImpl<Value, Source>& source) const {
return Gen(source.self(), limit_);
}
/**
* Convenience function for finite cycles used like:
*
* auto tripled = gen | cycle(3);
*/
Cycle<false> operator()(off_t limit) const { return Cycle<false>(limit); }
};
/*
******************************* Sinks *****************************************
*/
/**
* FoldLeft - Left-associative functional fold. For producing an aggregate value
* from a seed and a folder function. Useful for custom aggregators on a
* sequence.
*
* This type is primarily used through the 'foldl' helper method, like:
*
* double movingAverage = from(values)
* | foldl(0.0, [](double avg, double sample) {
* return sample * 0.1 + avg * 0.9;
* });
*/
template <class Seed, class Fold>
class FoldLeft : public Operator<FoldLeft<Seed, Fold>> {
Seed seed_;
Fold fold_;
public:
FoldLeft() = default;
FoldLeft(Seed seed, Fold fold)
: seed_(std::move(seed)), fold_(std::move(fold)) {}
template <class Source, class Value>
Seed compose(const GenImpl<Value, Source>& source) const {
static_assert(!Source::infinite, "Cannot foldl infinite source");
Seed accum = seed_;
source | [&](Value v) {
accum = fold_(std::move(accum), std::forward<Value>(v));
};
return accum;
}
};
/**
* First - For finding the first value in a sequence.
*
* This type is primarily used through the 'first' static value, like:
*
* int firstThreeDigitPrime = seq(100) | filter(isPrime) | first;
*/
class First : public Operator<First> {
public:
First() = default;
template <class Source,
class Value,
class StorageType = typename std::decay<Value>::type>
StorageType compose(const GenImpl<Value, Source>& source) const {
Optional<StorageType> accum;
source | [&](Value v) -> bool {
accum = std::forward<Value>(v);
return false;
};
if (!accum.hasValue()) {
throw EmptySequence();
}
return std::move(accum.value());
}
};
/**
* IsEmpty - a helper class for isEmpty and notEmpty
*
* Essentially returns 'result' if the source is empty. Note that this cannot be
* called on an infinite source, because then there is only one possible return
* value.
*
*
* Used primarily through 'isEmpty' and 'notEmpty' static values
*
* bool hasPrimes = g | filter(prime) | notEmpty;
* bool lacksEvens = g | filter(even) | isEmpty;
*
* Also used in the implementation of 'any' and 'all'
*/
template <bool emptyResult>
class IsEmpty : public Operator<IsEmpty<emptyResult>> {
public:
IsEmpty() = default;
template <class Source, class Value>
bool compose(const GenImpl<Value, Source>& source) const {
static_assert(!Source::infinite,
"Cannot call 'all', 'any', 'isEmpty', or 'notEmpty' on "
"infinite source. 'all' and 'isEmpty' will either return "
"false or hang. 'any' or 'notEmpty' will either return true "
"or hang.");
bool ans = emptyResult;
source | [&](Value v) -> bool {
ans = !emptyResult;
return false;
};
return ans;
}
};
/**
* Reduce - Functional reduce, for recursively combining values from a source
* using a reducer function until there is only one item left. Useful for
* combining values when an empty sequence doesn't make sense.
*
* This type is primarily used through the 'reduce' helper method, like:
*
* sring longest = from(names)
* | reduce([](string&& best, string& current) {
* return best.size() >= current.size() ? best : current;
* });
*/
template <class Reducer>
class Reduce : public Operator<Reduce<Reducer>> {
Reducer reducer_;
public:
Reduce() = default;
explicit Reduce(Reducer reducer) : reducer_(std::move(reducer)) {}
template <class Source,
class Value,
class StorageType = typename std::decay<Value>::type>
StorageType compose(const GenImpl<Value, Source>& source) const {
static_assert(!Source::infinite, "Cannot reduce infinite source");
Optional<StorageType> accum;
source | [&](Value v) {
if (accum.hasValue()) {
accum = reducer_(std::move(accum.value()), std::forward<Value>(v));
} else {
accum = std::forward<Value>(v);
}
};
if (!accum.hasValue()) {
throw EmptySequence();
}
return accum.value();
}
};
// not actually infinite, since an empty generator will end the cycles.
static constexpr bool infinite = Source::infinite;
};
/**
* Count - for simply counting the items in a collection.
*
* This type is usually used through its singleton, 'count':
*
* auto shortPrimes = seq(1, 100) | filter(isPrime) | count;
*/
class Count : public Operator<Count> {
public:
Count() = default;
template<class Source,
class Value,
class Gen = Generator<Value, Source>>
Gen compose(GenImpl<Value, Source>&& source) const {
return Gen(std::move(source.self()), limit_);
template <class Source, class Value>
size_t compose(const GenImpl<Value, Source>& source) const {
static_assert(!Source::infinite, "Cannot count infinite source");
return foldl(size_t(0),
[](size_t accum, Value v) {
return accum + 1;
}).compose(source);
}
};
/**
* Sum - For simply summing up all the values from a source.
*
* This type is usually used through its singleton, 'sum':
*
* auto gaussSum = seq(1, 100) | sum;
*/
class Sum : public Operator<Sum> {
public:
Sum() = default;
template<class Source,
template <class Source,
class Value,
class Gen = Generator<Value, Source>>
Gen compose(const GenImpl<Value, Source>& source) const {
return Gen(source.self(), limit_);
class StorageType = typename std::decay<Value>::type>
StorageType compose(const GenImpl<Value, Source>& source) const {
static_assert(!Source::infinite, "Cannot sum infinite source");
return foldl(StorageType(0),
[](StorageType&& accum, Value v) {
return std::move(accum) + std::forward<Value>(v);
}).compose(source);
}
};
/**
* Convenience function for finite cycles used like:
/**
* Contains - For testing whether a value matching the given value is contained
* in a sequence.
*
* auto tripled = gen | cycle(3);
* This type should be used through the 'contains' helper method, like:
*
* bool contained = seq(1, 10) | map(square) | contains(49);
*/
Cycle<false> operator()(off_t limit) const {
return Cycle<false>(limit);
template <class Needle>
class Contains : public Operator<Contains<Needle>> {
Needle needle_;
public:
explicit Contains(Needle needle) : needle_(std::move(needle)) {}
template <class Source,
class Value,
class StorageType = typename std::decay<Value>::type>
bool compose(const GenImpl<Value, Source>& source) const {
static_assert(!Source::infinite,
"Calling contains on an infinite source might cause "
"an infinite loop.");
return !(source | [this](Value value) {
return !(needle_ == std::forward<Value>(value));
});
}
};
/**
* Dereference - For dereferencing a sequence of pointers while filtering out
* null pointers.
* Min - For a value which minimizes a key, where the key is determined by a
* given selector, and compared by given comparer.
*
* This type is usually used through the 'dereference' static value, like:
* This type is usually used through the singletone 'min' or through the helper
* functions 'minBy' and 'maxBy'.
*
* auto refs = from(ptrs) | dereference;
* auto oldest = from(people)
* | minBy([](Person& p) {
* return p.dateOfBirth;
* });
*/
class Dereference : public Operator<Dereference> {
public:
Dereference() = default;
template <class Selector, class Comparer>
class Min : public Operator<Min<Selector, Comparer>> {
Selector selector_;
Comparer comparer_;
template<class Value,
class Source,
class Result = decltype(*std::declval<Value>())>
class Generator : public GenImpl<Result, Generator<Value, Source, Result>> {
Source source_;
public:
explicit Generator(Source source)
: source_(std::move(source)) {}
Min() = default;
template<class Body>
void foreach(Body&& body) const {
source_.foreach([&](Value value) {
if (value) {
return body(*value);
}
});
}
explicit Min(Selector selector) : selector_(std::move(selector)) {}
template<class Handler>
bool apply(Handler&& handler) const {
return source_.apply([&](Value value) -> bool {
if (value) {
return handler(*value);
Min(Selector selector, Comparer comparer)
: selector_(std::move(selector)), comparer_(std::move(comparer)) {}
template <class Value,
class Source,
class StorageType = typename std::decay<Value>::type,
class Key = typename std::decay<
typename std::result_of<Selector(Value)>::type>::type>
StorageType compose(const GenImpl<Value, Source>& source) const {
static_assert(!Source::infinite,
"Calling min or max on an infinite source will cause "
"an infinite loop.");
Optional<StorageType> min;
Optional<Key> minKey;
source | [&](Value v) {
Key key = selector_(std::forward<Value>(v));
if (!minKey.hasValue() || comparer_(key, minKey.value())) {
minKey = key;
min = std::forward<Value>(v);
}
return true;
});
};
if (!min.hasValue()) {
throw EmptySequence();
}
return min.value();
}
};
// not actually infinite, since an empty generator will end the cycles.
static constexpr bool infinite = Source::infinite;
};
/**
* Append - For collecting values from a source into a given output container
* by appending.
*
* This type is usually used through the helper function 'appendTo', like:
*
* vector<int64_t> ids;
* from(results) | map([](Person& p) { return p.id })
* | appendTo(ids);
*/
template <class Collection>
class Append : public Operator<Append<Collection>> {
Collection* collection_;
template<class Source,
class Value,
class Gen = Generator<Value, Source>>
Gen compose(GenImpl<Value, Source>&& source) const {
return Gen(std::move(source.self()));
}
public:
explicit Append(Collection* collection) : collection_(collection) {}
template<class Source,
class Value,
class Gen = Generator<Value, Source>>
Gen compose(const GenImpl<Value, Source>& source) const {
return Gen(source.self());
template <class Value, class Source>
Collection& compose(const GenImpl<Value, Source>& source) const {
static_assert(!Source::infinite, "Cannot appendTo with infinite source");
source | [&](Value v) {
collection_->insert(collection_->end(), std::forward<Value>(v));
};
return *collection_;
}
};
/**
* Indirect - For producing a sequence of the addresses of the values in the
* input.
* Collect - For collecting values from a source in a collection of the desired
* type.
*
* This type is usually used through the 'indirect' static value, like:
* This type is usually used through the helper function 'as', like:
*
* auto ptrs = from(refs) | indirect;
* std::string upper = from(stringPiece)
* | map(&toupper)
* | as<std::string>();
*/
class Indirect : public Operator<Indirect> {
template <class Collection>
class Collect : public Operator<Collect<Collection>> {
public:
Indirect() = default;
Collect() = default;
template <class Value,
class Source,
class Result = typename std::remove_reference<Value>::type*>
class Generator : public GenImpl<Result, Generator<Value, Source, Result>> {
Source source_;
static_assert(!std::is_rvalue_reference<Value>::value,
"Cannot use indirect on an rvalue");
public:
explicit Generator(Source source) : source_(std::move(source)) {}
template <class Body>
void foreach(Body&& body) const {
source_.foreach([&](Value value) {
return body(&value);
});
class StorageType = typename std::decay<Value>::type>
Collection compose(const GenImpl<Value, Source>& source) const {
static_assert(!Source::infinite,
"Cannot convert infinite source to object with as.");
Collection collection;
source | [&](Value v) {
collection.insert(collection.end(), std::forward<Value>(v));
};
return collection;
}
};
template <class Handler>
bool apply(Handler&& handler) const {
return source_.apply([&](Value value) -> bool {
return handler(&value);
});
}
/**
* CollectTemplate - For collecting values from a source in a collection
* constructed using the specified template type. Given the type of values
* produced by the given generator, the collection type will be:
* Container<Value, Allocator<Value>>
*
* The allocator defaults to std::allocator, so this may be used for the STL
* containers by simply using operators like 'as<set>', 'as<deque>',
* 'as<vector>'. 'as', here is the helper method which is the usual means of
* consturcting this operator.
*
* Example:
*
* set<string> uniqueNames = from(names) | as<set>();
*/
template <template <class, class> class Container,
template <class> class Allocator>
class CollectTemplate : public Operator<CollectTemplate<Container, Allocator>> {
public:
CollectTemplate() = default;
// not actually infinite, since an empty generator will end the cycles.
static constexpr bool infinite = Source::infinite;
template <class Value,
class Source,
class StorageType = typename std::decay<Value>::type,
class Collection = Container<StorageType, Allocator<StorageType>>>
Collection compose(const GenImpl<Value, Source>& source) const {
static_assert(!Source::infinite,
"Cannot convert infinite source to object with as.");
Collection collection;
source | [&](Value v) {
collection.insert(collection.end(), std::forward<Value>(v));
};
template <class Source, class Value, class Gen = Generator<Value, Source>>
Gen compose(GenImpl<Value, Source>&& source) const {
return Gen(std::move(source.self()));
}
template <class Source, class Value, class Gen = Generator<Value, Source>>
Gen compose(const GenImpl<Value, Source>& source) const {
return Gen(source.self());
return collection;
}
};
......@@ -2022,7 +2098,7 @@ class Indirect : public Operator<Indirect> {
/**
* VirtualGen<T> - For wrapping template types in simple polymorphic wrapper.
**/
template<class Value>
template <class Value>
class VirtualGen : public GenImpl<Value, VirtualGen<Value>> {
class WrapperBase {
public:
......@@ -2032,13 +2108,12 @@ class VirtualGen : public GenImpl<Value, VirtualGen<Value>> {
virtual std::unique_ptr<const WrapperBase> clone() const = 0;
};
template<class Wrapped>
template <class Wrapped>
class WrapperImpl : public WrapperBase {
Wrapped wrapped_;
public:
explicit WrapperImpl(Wrapped wrapped)
: wrapped_(std::move(wrapped)) {
}
explicit WrapperImpl(Wrapped wrapped) : wrapped_(std::move(wrapped)) {}
virtual bool apply(const std::function<bool(Value)>& handler) const {
return wrapped_.apply(handler);
......@@ -2063,8 +2138,7 @@ class VirtualGen : public GenImpl<Value, VirtualGen<Value>> {
VirtualGen(VirtualGen&& source) noexcept
: wrapper_(std::move(source.wrapper_)) {}
VirtualGen(const VirtualGen& source)
: wrapper_(source.wrapper_->clone()) {}
VirtualGen(const VirtualGen& source) : wrapper_(source.wrapper_->clone()) {}
VirtualGen& operator=(const VirtualGen& source) {
wrapper_.reset(source.wrapper_->clone());
......@@ -2072,7 +2146,7 @@ class VirtualGen : public GenImpl<Value, VirtualGen<Value>> {
}
VirtualGen& operator=(VirtualGen&& source) noexcept {
wrapper_= std::move(source.wrapper_);
wrapper_ = std::move(source.wrapper_);
return *this;
}
......@@ -2095,12 +2169,6 @@ constexpr detail::Count count{};
constexpr detail::First first{};
/**
* Use 'isEmpty' and 'notEmpty' for detecting if there are any values or not.
*
* bool hasPrimes = g | filter(prime) | notEmpty;
* bool lacksEvens = g | filter(even) | isEmpty;
*/
constexpr detail::IsEmpty<true> isEmpty{};
constexpr detail::IsEmpty<false> notEmpty{};
......@@ -2125,27 +2193,21 @@ constexpr detail::Dereference dereference{};
constexpr detail::Indirect indirect{};
inline detail::Take take(size_t count) {
return detail::Take(count);
}
inline detail::Take take(size_t count) { return detail::Take(count); }
inline detail::Stride stride(size_t s) {
return detail::Stride(s);
}
inline detail::Stride stride(size_t s) { return detail::Stride(s); }
template<class Random = std::default_random_engine>
template <class Random = std::default_random_engine>
inline detail::Sample<Random> sample(size_t count, Random rng = Random()) {
return detail::Sample<Random>(count, std::move(rng));
}
inline detail::Skip skip(size_t count) {
return detail::Skip(count);
}
inline detail::Skip skip(size_t count) { return detail::Skip(count); }
inline detail::Batch batch(size_t batchSize) {
return detail::Batch(batchSize);
}
}} //folly::gen
}} // folly::gen
#pragma GCC diagnostic pop
......@@ -181,6 +181,13 @@ class GenImpl : public FBounded<Self> {
// Child classes should override if the sequence generated is *definitely*
// infinite. 'infinite' may be false_type for some infinite sequences
// (due the the Halting Problem).
//
// In general, almost all sources are finite (only seq(n) produces an infinite
// source), almost all operators keep the finiteness of the source (only cycle
// makes an infinite generator from a finite one, only until and take make a
// finite generator from an infinite one, and concat needs both the inner and
// outer generators to be finite to make a finite one), and most sinks
// cannot accept and infinite generators (first being the expection).
static constexpr bool infinite = false;
};
......
......@@ -577,6 +577,37 @@ TEST(Gen, DistinctMove) { // 0 1 4 9 6 5 6 9 4 1 0
EXPECT_EQ(expected, actual);
}
TEST(Gen, DistinctInfinite) {
// distinct should be able to handle an infinite sequence, provided that, of
// of cource, is it eventually made finite before returning the result.
auto expected = seq(0) | take(5) | as<vector>(); // 0 1 2 3 4
auto actual =
seq(0) // 0 1 2 3 4 5 6 7 ...
| mapped([](int i) { return i / 2; }) // 0 0 1 1 2 2 3 3 ...
| distinct // 0 1 2 3 4 5 6 7 ...
| take(5) // 0 1 2 3 4
| as<vector>();
EXPECT_EQ(expected, actual);
}
TEST(Gen, DistinctByInfinite) {
// Similarly to the DistinctInfinite test case, distinct by should be able to
// handle infinite sequences. Note that depending on how many values we take()
// at the end, the sequence may infinite loop. This is fine becasue we cannot
// solve the halting problem.
auto expected = vector<int>{1, 2};
auto actual =
seq(1) // 1 2 3 4 5 6 7 8 ...
| distinctBy([](int i) { return i % 2; }) // 1 2 (but might by infinite)
| take(2) // 1 2
| as<vector>();
// Note that if we had take(3), this would infinite loop
EXPECT_EQ(expected, actual);
}
TEST(Gen, MinBy) {
EXPECT_EQ(7, seq(1, 10)
| minBy([](int i) -> double {
......@@ -743,21 +774,17 @@ TEST(Gen, Get) {
}
TEST(Gen, notEmpty) {
EXPECT_TRUE(seq(0) | notEmpty);
EXPECT_TRUE(seq(0, 1) | notEmpty);
EXPECT_TRUE(just(1) | notEmpty);
EXPECT_FALSE(gen::range(0, 0) | notEmpty);
EXPECT_FALSE(from({1}) | take(0) | notEmpty);
EXPECT_TRUE(seq(1, 3) | cycle | notEmpty);
}
TEST(Gen, isEmpty) {
EXPECT_FALSE(seq(0) | isEmpty);
EXPECT_FALSE(seq(0, 1) | isEmpty);
EXPECT_FALSE(just(1) | isEmpty);
EXPECT_TRUE(gen::range(0, 0) | isEmpty);
EXPECT_TRUE(from({1}) | take(0) | isEmpty);
EXPECT_FALSE(seq(1, 3) | cycle | isEmpty);
}
TEST(Gen, Any) {
......@@ -1018,7 +1045,8 @@ TEST(Gen, Cycle) {
};
auto s = countdown;
EXPECT_EQ((vector<int> { 1, 2, 3, 1, 2, 1}),
s | cycle | as<vector>());
s | cycle | take(7) | as<vector>());
// take necessary as cycle returns an infinite generator
}
}
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment