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 ...@@ -37,6 +37,9 @@ struct ArgumentReference
T&& // int -> int&& T&& // int -> int&&
>::type> {}; >::type> {};
/**
* Group - The output objects from the GroupBy operator
*/
template <class Key, class Value> template <class Key, class Value>
class Group : public GenImpl<Value&&, Group<Key, Value>> { class Group : public GenImpl<Value&&, Group<Key, Value>> {
public: public:
...@@ -82,6 +85,9 @@ class Group : public GenImpl<Value&&, Group<Key, Value>> { ...@@ -82,6 +85,9 @@ class Group : public GenImpl<Value&&, Group<Key, Value>> {
return true; return true;
} }
// GroupBy only takes in finite generators, so we only have finite groups
static constexpr bool infinite = false;
private: private:
Key key_; Key key_;
mutable VectorType values_; mutable VectorType values_;
...@@ -89,6 +95,12 @@ class Group : public GenImpl<Value&&, Group<Key, Value>> { ...@@ -89,6 +95,12 @@ class Group : public GenImpl<Value&&, Group<Key, Value>> {
namespace detail { namespace detail {
// Classes used for the implementation of Sources, Operators, and Sinks
/*
******************************* Sources ***************************************
*/
/* /*
* ReferencedSource - Generate values from an STL-like container using * ReferencedSource - Generate values from an STL-like container using
* iterators from .begin() until .end(). Value type defaults to the type of * iterators from .begin() until .end(). Value type defaults to the type of
...@@ -101,23 +113,22 @@ namespace detail { ...@@ -101,23 +113,22 @@ namespace detail {
* string& longestName = from(names) * string& longestName = from(names)
* | maxBy([](string& s) { return s.size() }); * | maxBy([](string& s) { return s.size() });
*/ */
template<class Container, template <class Container, class Value>
class Value> class ReferencedSource
class ReferencedSource : : public GenImpl<Value, ReferencedSource<Container, Value>> {
public GenImpl<Value, ReferencedSource<Container, Value>> {
Container* container_; 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 { void foreach(Body&& body) const {
for (auto& value : *container_) { for (auto& value : *container_) {
body(std::forward<Value>(value)); body(std::forward<Value>(value));
} }
} }
template<class Handler> template <class Handler>
bool apply(Handler&& handler) const { bool apply(Handler&& handler) const {
for (auto& value : *container_) { for (auto& value : *container_) {
if (!handler(std::forward<Value>(value))) { if (!handler(std::forward<Value>(value))) {
...@@ -126,6 +137,9 @@ public: ...@@ -126,6 +137,9 @@ public:
} }
return true; return true;
} }
// from takes in a normal stl structure, which are all finite
static constexpr bool infinite = false;
}; };
/** /**
...@@ -142,45 +156,44 @@ public: ...@@ -142,45 +156,44 @@ public:
* *
* Though it is also used for the initializer_list specialization of from(). * Though it is also used for the initializer_list specialization of from().
*/ */
template<class StorageType, template <class StorageType, class Container>
class Container> class CopiedSource
class CopiedSource : : public GenImpl<const StorageType&, CopiedSource<StorageType, Container>> {
public GenImpl<const StorageType&, static_assert(!std::is_reference<StorageType>::value,
CopiedSource<StorageType, Container>> { "StorageType must be decayed");
static_assert(
!std::is_reference<StorageType>::value, "StorageType must be decayed");
public: public:
// Generator objects are often copied during normal construction as they are // Generator objects are often copied during normal construction as they are
// encapsulated by downstream generators. It would be bad if this caused // 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 // 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 // const reference to the value, it's safe to share it between multiple
// generators. // generators.
static_assert( static_assert(!std::is_reference<Container>::value,
!std::is_reference<Container>::value,
"Can't copy into a reference"); "Can't copy into a reference");
std::shared_ptr<const Container> copy_; std::shared_ptr<const Container> copy_;
public:
public:
typedef Container ContainerType; typedef Container ContainerType;
template<class SourceContainer> template <class SourceContainer>
explicit CopiedSource(const SourceContainer& container) explicit CopiedSource(const SourceContainer& container)
: copy_(new Container(begin(container), end(container))) {} : copy_(new Container(begin(container), end(container))) {}
explicit CopiedSource(Container&& container) : explicit CopiedSource(Container&& container)
copy_(new Container(std::move(container))) {} : copy_(new Container(std::move(container))) {}
// To enable re-use of cached results. // To enable re-use of cached results.
CopiedSource(const CopiedSource<StorageType, Container>& source) CopiedSource(const CopiedSource<StorageType, Container>& source)
: copy_(source.copy_) {} : copy_(source.copy_) {}
template<class Body> template <class Body>
void foreach(Body&& body) const { void foreach(Body&& body) const {
for (const auto& value : *copy_) { for (const auto& value : *copy_) {
body(value); body(value);
} }
} }
template<class Handler> template <class Handler>
bool apply(Handler&& handler) const { bool apply(Handler&& handler) const {
// The collection may be reused by others, we can't allow it to be changed. // The collection may be reused by others, we can't allow it to be changed.
for (const auto& value : *copy_) { for (const auto& value : *copy_) {
...@@ -190,6 +203,9 @@ public: ...@@ -190,6 +203,9 @@ public:
} }
return true; return true;
} }
// from takes in a normal stl structure, which are all finite
static constexpr bool infinite = false;
}; };
/** /**
...@@ -203,17 +219,16 @@ public: ...@@ -203,17 +219,16 @@ public:
* *
* Reminder: Be careful not to invalidate iterators when using ranges like this. * 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, class RangeSource : public GenImpl<typename Range<Iterator>::reference,
RangeSource<Iterator>> { RangeSource<Iterator>> {
Range<Iterator> range_; Range<Iterator> range_;
public: public:
RangeSource() = default; RangeSource() = default;
explicit RangeSource(Range<Iterator> range) explicit RangeSource(Range<Iterator> range) : range_(std::move(range)) {}
: range_(std::move(range))
{}
template<class Handler> template <class Handler>
bool apply(Handler&& handler) const { bool apply(Handler&& handler) const {
for (auto& value : range_) { for (auto& value : range_) {
if (!handler(value)) { if (!handler(value)) {
...@@ -223,12 +238,15 @@ class RangeSource : public GenImpl<typename Range<Iterator>::reference, ...@@ -223,12 +238,15 @@ class RangeSource : public GenImpl<typename Range<Iterator>::reference,
return true; return true;
} }
template<class Body> template <class Body>
void foreach(Body&& body) const { void foreach(Body&& body) const {
for (auto& value : range_) { for (auto& value : range_) {
body(value); 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, ...@@ -242,17 +260,19 @@ class RangeSource : public GenImpl<typename Range<Iterator>::reference,
* auto indexes = range(0, 10); * auto indexes = range(0, 10);
* auto endless = seq(0); // 0, 1, 2, 3, ... * 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>> { class Sequence : public GenImpl<const Value&, Sequence<Value, SequenceImpl>> {
static_assert(!std::is_reference<Value>::value && 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_; Value start_;
SequenceImpl impl_; SequenceImpl impl_;
public:
public:
explicit Sequence(Value start, SequenceImpl impl) 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 { bool apply(Handler&& handler) const {
for (Value current = start_; impl_.test(current); impl_.step(current)) { for (Value current = start_; impl_.test(current); impl_.step(current)) {
if (!handler(current)) { if (!handler(current)) {
...@@ -262,70 +282,82 @@ public: ...@@ -262,70 +282,82 @@ public:
return true; return true;
} }
template<class Body> template <class Body>
void foreach(Body&& body) const { void foreach(Body&& body) const {
for (Value current = start_; impl_.test(current); impl_.step(current)) { for (Value current = start_; impl_.test(current); impl_.step(current)) {
body(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) * Sequence implementations (range, sequence, infinite, with/without step)
**/ **/
template<class Value> template <class Value>
class RangeImpl { class RangeImpl {
Value end_; Value end_;
public: 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_; } bool test(const Value& current) const { return current < end_; }
void step(Value& current) const { ++current; } void step(Value& current) const { ++current; }
static constexpr bool infinite = false;
}; };
template<class Value, class Distance> template <class Value, class Distance>
class RangeWithStepImpl { class RangeWithStepImpl {
Value end_; Value end_;
Distance step_; Distance step_;
public: public:
explicit RangeWithStepImpl(Value end, Distance step) 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_; } bool test(const Value& current) const { return current < end_; }
void step(Value& current) const { current += step_; } void step(Value& current) const { current += step_; }
static constexpr bool infinite = false;
}; };
template<class Value> template <class Value>
class SeqImpl { class SeqImpl {
Value end_; Value end_;
public: 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_; } bool test(const Value& current) const { return current <= end_; }
void step(Value& current) const { ++current; } void step(Value& current) const { ++current; }
static constexpr bool infinite = false;
}; };
template<class Value, class Distance> template <class Value, class Distance>
class SeqWithStepImpl { class SeqWithStepImpl {
Value end_; Value end_;
Distance step_; Distance step_;
public: public:
explicit SeqWithStepImpl(Value end, Distance step) 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_; } bool test(const Value& current) const { return current <= end_; }
void step(Value& current) const { current += step_; } void step(Value& current) const { current += step_; }
static constexpr bool infinite = false;
}; };
template<class Value> template <class Value>
class InfiniteImpl { class InfiniteImpl {
public: public:
bool test(const Value& current) const { return true; } bool test(const Value& current) const { return true; }
void step(Value& current) const { ++current; } void step(Value& current) const { ++current; }
static constexpr bool infinite = true;
}; };
/** /**
* GenratorBuilder - Helper for GENERTATOR macro. * GenratorBuilder - Helper for GENERTATOR macro.
**/ **/
template<class Value> template <class Value>
struct GeneratorBuilder { struct GeneratorBuilder {
template<class Source, template <class Source,
class Yield = detail::Yield<Value, Source>> class Yield = detail::Yield<Value, Source>>
Yield operator+(Source&& source) { Yield operator+(Source&& source) {
return Yield(std::forward<Source>(source)); return Yield(std::forward<Source>(source));
...@@ -336,15 +368,14 @@ struct GeneratorBuilder { ...@@ -336,15 +368,14 @@ struct GeneratorBuilder {
* Yield - For producing values from a user-defined generator by way of a * Yield - For producing values from a user-defined generator by way of a
* 'yield' function. * 'yield' function.
**/ **/
template<class Value, class Source> template <class Value, class Source>
class Yield : public GenImpl<Value, Yield<Value, Source>> { class Yield : public GenImpl<Value, Yield<Value, Source>> {
Source source_; Source source_;
public: public:
explicit Yield(Source source) explicit Yield(Source source) : source_(std::move(source)) {}
: source_(std::move(source)) {
}
template<class Handler> template <class Handler>
bool apply(Handler&& handler) const { bool apply(Handler&& handler) const {
struct Break {}; struct Break {};
auto body = [&](Value value) { auto body = [&](Value value) {
...@@ -360,13 +391,13 @@ class Yield : public GenImpl<Value, Yield<Value, Source>> { ...@@ -360,13 +391,13 @@ class Yield : public GenImpl<Value, Yield<Value, Source>> {
} }
} }
template<class Body> template <class Body>
void foreach(Body&& body) const { void foreach(Body&& body) const {
source_(std::forward<Body>(body)); source_(std::forward<Body>(body));
} }
}; };
template<class Value> template <class Value>
class Empty : public GenImpl<Value, Empty<Value>> { class Empty : public GenImpl<Value, Empty<Value>> {
public: public:
template <class Handler> template <class Handler>
...@@ -376,6 +407,9 @@ class Empty : public GenImpl<Value, Empty<Value>> { ...@@ -376,6 +407,9 @@ class Empty : public GenImpl<Value, Empty<Value>> {
template <class Body> template <class Body>
void foreach(Body&&) const {} void foreach(Body&&) const {}
// No values, so finite
static constexpr bool infinite = false;
}; };
template <class Value> template <class Value>
...@@ -383,6 +417,7 @@ class SingleReference : public GenImpl<Value&, SingleReference<Value>> { ...@@ -383,6 +417,7 @@ class SingleReference : public GenImpl<Value&, SingleReference<Value>> {
static_assert(!std::is_reference<Value>::value, static_assert(!std::is_reference<Value>::value,
"SingleReference requires non-ref types"); "SingleReference requires non-ref types");
Value* ptr_; Value* ptr_;
public: public:
explicit SingleReference(Value& ref) : ptr_(&ref) {} explicit SingleReference(Value& ref) : ptr_(&ref) {}
...@@ -395,6 +430,9 @@ class SingleReference : public GenImpl<Value&, SingleReference<Value>> { ...@@ -395,6 +430,9 @@ class SingleReference : public GenImpl<Value&, SingleReference<Value>> {
void foreach(Body&& body) const { void foreach(Body&& body) const {
body(*ptr_); body(*ptr_);
} }
// One value, so finite
static constexpr bool infinite = false;
}; };
template <class Value> template <class Value>
...@@ -402,6 +440,7 @@ class SingleCopy : public GenImpl<const Value&, SingleCopy<Value>> { ...@@ -402,6 +440,7 @@ class SingleCopy : public GenImpl<const Value&, SingleCopy<Value>> {
static_assert(!std::is_reference<Value>::value, static_assert(!std::is_reference<Value>::value,
"SingleCopy requires non-ref types"); "SingleCopy requires non-ref types");
Value value_; Value value_;
public: public:
explicit SingleCopy(Value value) : value_(std::forward<Value>(value)) {} explicit SingleCopy(Value value) : value_(std::forward<Value>(value)) {}
...@@ -414,10 +453,13 @@ class SingleCopy : public GenImpl<const Value&, SingleCopy<Value>> { ...@@ -414,10 +453,13 @@ class SingleCopy : public GenImpl<const Value&, SingleCopy<Value>> {
void foreach(Body&& body) const { void foreach(Body&& body) const {
body(value_); 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>> { ...@@ -428,37 +470,34 @@ class SingleCopy : public GenImpl<const Value&, SingleCopy<Value>> {
* *
* auto squares = seq(1, 10) | map(square) | asVector; * auto squares = seq(1, 10) | map(square) | asVector;
*/ */
template<class Predicate> template <class Predicate>
class Map : public Operator<Map<Predicate>> { class Map : public Operator<Map<Predicate>> {
Predicate pred_; Predicate pred_;
public: public:
Map() = default; Map() = default;
explicit Map(Predicate pred) explicit Map(Predicate pred) : pred_(std::move(pred)) {}
: pred_(std::move(pred))
{ }
template<class Value, template <class Value,
class Source, class Source,
class Result = typename ArgumentReference< class Result = typename ArgumentReference<
typename std::result_of<Predicate(Value)>::type typename std::result_of<Predicate(Value)>::type>::type>
>::type> class Generator : public GenImpl<Result, Generator<Value, Source, Result>> {
class Generator :
public GenImpl<Result, Generator<Value, Source, Result>> {
Source source_; Source source_;
Predicate pred_; Predicate pred_;
public: public:
explicit Generator(Source source, const Predicate& pred) explicit Generator(Source source, const Predicate& pred)
: source_(std::move(source)), pred_(pred) {} : source_(std::move(source)), pred_(pred) {}
template<class Body> template <class Body>
void foreach(Body&& body) const { void foreach(Body&& body) const {
source_.foreach([&](Value value) { source_.foreach(
body(pred_(std::forward<Value>(value))); [&](Value value) { body(pred_(std::forward<Value>(value))); });
});
} }
template<class Handler> template <class Handler>
bool apply(Handler&& handler) const { bool apply(Handler&& handler) const {
return source_.apply([&](Value value) { return source_.apply([&](Value value) {
return handler(pred_(std::forward<Value>(value))); return handler(pred_(std::forward<Value>(value)));
...@@ -468,14 +507,14 @@ class Map : public Operator<Map<Predicate>> { ...@@ -468,14 +507,14 @@ class Map : public Operator<Map<Predicate>> {
static constexpr bool infinite = Source::infinite; static constexpr bool infinite = Source::infinite;
}; };
template<class Source, template <class Source,
class Value, class Value,
class Gen = Generator<Value, Source>> class Gen = Generator<Value, Source>>
Gen compose(GenImpl<Value, Source>&& source) const { Gen compose(GenImpl<Value, Source>&& source) const {
return Gen(std::move(source.self()), pred_); return Gen(std::move(source.self()), pred_);
} }
template<class Source, template <class Source,
class Value, class Value,
class Gen = Generator<Value, Source>> class Gen = Generator<Value, Source>>
Gen compose(const GenImpl<Value, Source>& source) const { Gen compose(const GenImpl<Value, Source>& source) const {
...@@ -500,25 +539,24 @@ class Map : public Operator<Map<Predicate>> { ...@@ -500,25 +539,24 @@ class Map : public Operator<Map<Predicate>> {
* *
* will give a vector of all the pointers != nullptr. * will give a vector of all the pointers != nullptr.
*/ */
template<class Predicate> template <class Predicate>
class Filter : public Operator<Filter<Predicate>> { class Filter : public Operator<Filter<Predicate>> {
Predicate pred_; Predicate pred_;
public: public:
Filter() = default; Filter() = default;
explicit Filter(Predicate pred) explicit Filter(Predicate pred) : pred_(std::move(pred)) {}
: pred_(std::move(pred))
{ }
template<class Value, template <class Value, class Source>
class Source>
class Generator : public GenImpl<Value, Generator<Value, Source>> { class Generator : public GenImpl<Value, Generator<Value, Source>> {
Source source_; Source source_;
Predicate pred_; Predicate pred_;
public: public:
explicit Generator(Source source, const Predicate& pred) explicit Generator(Source source, const Predicate& pred)
: source_(std::move(source)), pred_(pred) {} : source_(std::move(source)), pred_(pred) {}
template<class Body> template <class Body>
void foreach(Body&& body) const { void foreach(Body&& body) const {
source_.foreach([&](Value value) { source_.foreach([&](Value value) {
if (pred_(std::forward<Value>(value))) { if (pred_(std::forward<Value>(value))) {
...@@ -527,7 +565,7 @@ class Filter : public Operator<Filter<Predicate>> { ...@@ -527,7 +565,7 @@ class Filter : public Operator<Filter<Predicate>> {
}); });
} }
template<class Handler> template <class Handler>
bool apply(Handler&& handler) const { bool apply(Handler&& handler) const {
return source_.apply([&](Value value) -> bool { return source_.apply([&](Value value) -> bool {
if (pred_(std::forward<Value>(value))) { if (pred_(std::forward<Value>(value))) {
...@@ -540,14 +578,14 @@ class Filter : public Operator<Filter<Predicate>> { ...@@ -540,14 +578,14 @@ class Filter : public Operator<Filter<Predicate>> {
static constexpr bool infinite = Source::infinite; static constexpr bool infinite = Source::infinite;
}; };
template<class Source, template <class Source,
class Value, class Value,
class Gen = Generator<Value, Source>> class Gen = Generator<Value, Source>>
Gen compose(GenImpl<Value, Source>&& source) const { Gen compose(GenImpl<Value, Source>&& source) const {
return Gen(std::move(source.self()), pred_); return Gen(std::move(source.self()), pred_);
} }
template<class Source, template <class Source,
class Value, class Value,
class Gen = Generator<Value, Source>> class Gen = Generator<Value, Source>>
Gen compose(const GenImpl<Value, Source>& source) const { Gen compose(const GenImpl<Value, Source>& source) const {
...@@ -564,25 +602,24 @@ class Filter : public Operator<Filter<Predicate>> { ...@@ -564,25 +602,24 @@ class Filter : public Operator<Filter<Predicate>> {
* | until([](Item& item) { return item.score > 100; }) * | until([](Item& item) { return item.score > 100; })
* | asVector; * | asVector;
*/ */
template<class Predicate> template <class Predicate>
class Until : public Operator<Until<Predicate>> { class Until : public Operator<Until<Predicate>> {
Predicate pred_; Predicate pred_;
public: public:
Until() = default; Until() = default;
explicit Until(Predicate pred) explicit Until(Predicate pred) : pred_(std::move(pred)) {}
: pred_(std::move(pred))
{}
template<class Value, template <class Value, class Source>
class Source>
class Generator : public GenImpl<Value, Generator<Value, Source>> { class Generator : public GenImpl<Value, Generator<Value, Source>> {
Source source_; Source source_;
Predicate pred_; Predicate pred_;
public: public:
explicit Generator(Source source, const Predicate& pred) explicit Generator(Source source, const Predicate& pred)
: source_(std::move(source)), pred_(pred) {} : source_(std::move(source)), pred_(pred) {}
template<class Handler> template <class Handler>
bool apply(Handler&& handler) const { bool apply(Handler&& handler) const {
bool cancelled = false; bool cancelled = false;
source_.apply([&](Value value) -> bool { source_.apply([&](Value value) -> bool {
...@@ -597,24 +634,24 @@ class Until : public Operator<Until<Predicate>> { ...@@ -597,24 +634,24 @@ class Until : public Operator<Until<Predicate>> {
}); });
return !cancelled; return !cancelled;
} }
// Theoretically an 'until' might stop an infinite
static constexpr bool infinite = false;
}; };
template<class Source, template <class Source,
class Value, class Value,
class Gen = Generator<Value, Source>> class Gen = Generator<Value, Source>>
Gen compose(GenImpl<Value, Source>&& source) const { Gen compose(GenImpl<Value, Source>&& source) const {
return Gen(std::move(source.self()), pred_); return Gen(std::move(source.self()), pred_);
} }
template<class Source, template <class Source,
class Value, class Value,
class Gen = Generator<Value, Source>> class Gen = Generator<Value, Source>>
Gen compose(const GenImpl<Value, Source>& source) const { Gen compose(const GenImpl<Value, Source>& source) const {
return Gen(source.self(), pred_); 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>> { ...@@ -628,23 +665,24 @@ class Until : public Operator<Until<Predicate>> {
*/ */
class Take : public Operator<Take> { class Take : public Operator<Take> {
size_t count_; size_t count_;
public: public:
explicit Take(size_t count) explicit Take(size_t count) : count_(count) {}
: count_(count) {}
template<class Value, template <class Value, class Source>
class Source> class Generator : public GenImpl<Value, Generator<Value, Source>> {
class Generator :
public GenImpl<Value, Generator<Value, Source>> {
Source source_; Source source_;
size_t count_; size_t count_;
public: public:
explicit Generator(Source source, size_t count) 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 { bool apply(Handler&& handler) const {
if (count_ == 0) { return false; } if (count_ == 0) {
return false;
}
size_t n = count_; size_t n = count_;
bool cancelled = false; bool cancelled = false;
source_.apply([&](Value value) -> bool { source_.apply([&](Value value) -> bool {
...@@ -656,16 +694,19 @@ class Take : public Operator<Take> { ...@@ -656,16 +694,19 @@ class Take : public Operator<Take> {
}); });
return !cancelled; return !cancelled;
} }
// take will stop an infinite generator
static constexpr bool infinite = false;
}; };
template<class Source, template <class Source,
class Value, class Value,
class Gen = Generator<Value, Source>> class Gen = Generator<Value, Source>>
Gen compose(GenImpl<Value, Source>&& source) const { Gen compose(GenImpl<Value, Source>&& source) const {
return Gen(std::move(source.self()), count_); return Gen(std::move(source.self()), count_);
} }
template<class Source, template <class Source,
class Value, class Value,
class Gen = Generator<Value, Source>> class Gen = Generator<Value, Source>>
Gen compose(const GenImpl<Value, Source>& source) const { Gen compose(const GenImpl<Value, Source>& source) const {
...@@ -695,14 +736,15 @@ class Stride : public Operator<Stride> { ...@@ -695,14 +736,15 @@ class Stride : public Operator<Stride> {
class Generator : public GenImpl<Value, Generator<Value, Source>> { class Generator : public GenImpl<Value, Generator<Value, Source>> {
Source source_; Source source_;
size_t stride_; size_t stride_;
public: public:
Generator(Source source, size_t stride) explicit Generator(Source source, size_t stride)
: source_(std::move(source)), stride_(stride) {} : source_(std::move(source)), stride_(stride) {}
template <class Handler> template <class Handler>
bool apply(Handler&& handler) const { bool apply(Handler&& handler) const {
size_t distance = stride_; size_t distance = stride_;
return source_.apply([&](Value value)->bool { return source_.apply([&](Value value) -> bool {
if (++distance >= stride_) { if (++distance >= stride_) {
if (!handler(std::forward<Value>(value))) { if (!handler(std::forward<Value>(value))) {
return false; return false;
...@@ -723,14 +765,21 @@ class Stride : public Operator<Stride> { ...@@ -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 { Gen compose(GenImpl<Value, Source>&& source) const {
return Gen(std::move(source.self()), stride_); 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 { Gen compose(const GenImpl<Value, Source>& source) const {
return Gen(source.self(), stride_); return Gen(source.self(), stride_);
} }
...@@ -740,20 +789,21 @@ class Stride : public Operator<Stride> { ...@@ -740,20 +789,21 @@ class Stride : public Operator<Stride> {
* Sample - For taking a random sample of N elements from a sequence * Sample - For taking a random sample of N elements from a sequence
* (without replacement). * (without replacement).
*/ */
template<class Random> template <class Random>
class Sample : public Operator<Sample<Random>> { class Sample : public Operator<Sample<Random>> {
size_t count_; size_t count_;
Random rng_; Random rng_;
public: public:
explicit Sample(size_t count, Random rng) explicit Sample(size_t count, Random rng)
: count_(count), rng_(std::move(rng)) {} : count_(count), rng_(std::move(rng)) {}
template<class Value, template <class Value,
class Source, class Source,
class Rand, class Rand,
class StorageType = typename std::decay<Value>::type> class StorageType = typename std::decay<Value>::type>
class Generator : class Generator
public GenImpl<StorageType&&, : public GenImpl<StorageType&&,
Generator<Value, Source, Rand, StorageType>> { Generator<Value, Source, Rand, StorageType>> {
static_assert(!Source::infinite, "Cannot sample infinite source!"); static_assert(!Source::infinite, "Cannot sample infinite source!");
// It's too easy to bite ourselves if random generator is only 16-bit // It's too easy to bite ourselves if random generator is only 16-bit
...@@ -762,13 +812,16 @@ class Sample : public Operator<Sample<Random>> { ...@@ -762,13 +812,16 @@ class Sample : public Operator<Sample<Random>> {
Source source_; Source source_;
size_t count_; size_t count_;
mutable Rand rng_; mutable Rand rng_;
public: public:
explicit Generator(Source source, size_t count, Random rng) 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 { bool apply(Handler&& handler) const {
if (count_ == 0) { return false; } if (count_ == 0) {
return false;
}
std::vector<StorageType> v; std::vector<StorageType> v;
v.reserve(count_); v.reserve(count_);
// use reservoir sampling to give each source value an equal chance // use reservoir sampling to give each source value an equal chance
...@@ -790,23 +843,26 @@ class Sample : public Operator<Sample<Random>> { ...@@ -790,23 +843,26 @@ class Sample : public Operator<Sample<Random>> {
}); });
// output is unsorted! // output is unsorted!
for (auto& val: v) { for (auto& val : v) {
if (!handler(std::move(val))) { if (!handler(std::move(val))) {
return false; return false;
} }
} }
return true; return true;
} }
// Only takes N elements, so finite
static constexpr bool infinite = false;
}; };
template<class Source, template <class Source,
class Value, class Value,
class Gen = Generator<Value, Source, Random>> class Gen = Generator<Value, Source, Random>>
Gen compose(GenImpl<Value, Source>&& source) const { Gen compose(GenImpl<Value, Source>&& source) const {
return Gen(std::move(source.self()), count_, rng_); return Gen(std::move(source.self()), count_, rng_);
} }
template<class Source, template <class Source,
class Value, class Value,
class Gen = Generator<Value, Source, Random>> class Gen = Generator<Value, Source, Random>>
Gen compose(const GenImpl<Value, Source>& source) const { Gen compose(const GenImpl<Value, Source>& source) const {
...@@ -825,21 +881,20 @@ class Sample : public Operator<Sample<Random>> { ...@@ -825,21 +881,20 @@ class Sample : public Operator<Sample<Random>> {
*/ */
class Skip : public Operator<Skip> { class Skip : public Operator<Skip> {
size_t count_; size_t count_;
public: public:
explicit Skip(size_t count) explicit Skip(size_t count) : count_(count) {}
: count_(count) {}
template<class Value, template <class Value, class Source>
class Source> class Generator : public GenImpl<Value, Generator<Value, Source>> {
class Generator :
public GenImpl<Value, Generator<Value, Source>> {
Source source_; Source source_;
size_t count_; size_t count_;
public: public:
explicit Generator(Source source, size_t count) 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 { void foreach(Body&& body) const {
if (count_ == 0) { if (count_ == 0) {
source_.foreach(body); source_.foreach(body);
...@@ -855,7 +910,7 @@ class Skip : public Operator<Skip> { ...@@ -855,7 +910,7 @@ class Skip : public Operator<Skip> {
}); });
} }
template<class Handler> template <class Handler>
bool apply(Handler&& handler) const { bool apply(Handler&& handler) const {
if (count_ == 0) { if (count_ == 0) {
return source_.apply(std::forward<Handler>(handler)); return source_.apply(std::forward<Handler>(handler));
...@@ -870,17 +925,18 @@ class Skip : public Operator<Skip> { ...@@ -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; static constexpr bool infinite = Source::infinite;
}; };
template<class Source, template <class Source,
class Value, class Value,
class Gen = Generator<Value, Source>> class Gen = Generator<Value, Source>>
Gen compose(GenImpl<Value, Source>&& source) const { Gen compose(GenImpl<Value, Source>&& source) const {
return Gen(std::move(source.self()), count_); return Gen(std::move(source.self()), count_);
} }
template<class Source, template <class Source,
class Value, class Value,
class Gen = Generator<Value, Source>> class Gen = Generator<Value, Source>>
Gen compose(const GenImpl<Value, Source>& source) const { Gen compose(const GenImpl<Value, Source>& source) const {
...@@ -901,29 +957,25 @@ class Skip : public Operator<Skip> { ...@@ -901,29 +957,25 @@ class Skip : public Operator<Skip> {
* }) * })
* | take(10); * | take(10);
*/ */
template<class Selector, class Comparer> template <class Selector, class Comparer>
class Order : public Operator<Order<Selector, Comparer>> { class Order : public Operator<Order<Selector, Comparer>> {
Selector selector_; Selector selector_;
Comparer comparer_; Comparer comparer_;
public: public:
Order() = default; Order() = default;
explicit Order(Selector selector) explicit Order(Selector selector) : selector_(std::move(selector)) {}
: selector_(std::move(selector))
{}
Order(Selector selector, Order(Selector selector, Comparer comparer)
Comparer comparer) : selector_(std::move(selector)), comparer_(std::move(comparer)) {}
: selector_(std::move(selector))
, comparer_(std::move(comparer))
{}
template<class Value, template <class Value,
class Source, class Source,
class StorageType = typename std::decay<Value>::type, class StorageType = typename std::decay<Value>::type,
class Result = typename std::result_of<Selector(Value)>::type> class Result = typename std::result_of<Selector(Value)>::type>
class Generator : class Generator
public GenImpl<StorageType&&, : public GenImpl<StorageType&&,
Generator<Value, Source, StorageType, Result>> { Generator<Value, Source, StorageType, Result>> {
static_assert(!Source::infinite, "Cannot sort infinite source!"); static_assert(!Source::infinite, "Cannot sort infinite source!");
Source source_; Source source_;
...@@ -940,10 +992,9 @@ class Order : public Operator<Order<Selector, Comparer>> { ...@@ -940,10 +992,9 @@ class Order : public Operator<Order<Selector, Comparer>> {
std::sort(vals.begin(), vals.end(), comparer); std::sort(vals.begin(), vals.end(), comparer);
return std::move(vals); return std::move(vals);
} }
public: public:
Generator(Source source, Generator(Source source, Selector selector, Comparer comparer)
Selector selector,
Comparer comparer)
: source_(std::move(source)), : source_(std::move(source)),
selector_(std::move(selector)), selector_(std::move(selector)),
comparer_(std::move(comparer)) {} comparer_(std::move(comparer)) {}
...@@ -956,14 +1007,14 @@ class Order : public Operator<Order<Selector, Comparer>> { ...@@ -956,14 +1007,14 @@ class Order : public Operator<Order<Selector, Comparer>> {
return asVector(); return asVector();
} }
template<class Body> template <class Body>
void foreach(Body&& body) const { void foreach(Body&& body) const {
for (auto& value : asVector()) { for (auto& value : asVector()) {
body(std::move(value)); body(std::move(value));
} }
} }
template<class Handler> template <class Handler>
bool apply(Handler&& handler) const { bool apply(Handler&& handler) const {
auto comparer = [&](const StorageType& a, const StorageType& b) { auto comparer = [&](const StorageType& a, const StorageType& b) {
// swapped for minHeap // swapped for minHeap
...@@ -980,16 +1031,19 @@ class Order : public Operator<Order<Selector, Comparer>> { ...@@ -980,16 +1031,19 @@ class Order : public Operator<Order<Selector, Comparer>> {
} }
return true; 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 Value,
class Gen = Generator<Value, Source>> class Gen = Generator<Value, Source>>
Gen compose(GenImpl<Value, Source>&& source) const { Gen compose(GenImpl<Value, Source>&& source) const {
return Gen(std::move(source.self()), selector_, comparer_); return Gen(std::move(source.self()), selector_, comparer_);
} }
template<class Source, template <class Source,
class Value, class Value,
class Gen = Generator<Value, Source>> class Gen = Generator<Value, Source>>
Gen compose(const GenImpl<Value, Source>& source) const { Gen compose(const GenImpl<Value, Source>& source) const {
...@@ -1057,14 +1111,21 @@ class GroupBy : public Operator<GroupBy<Selector>> { ...@@ -1057,14 +1111,21 @@ class GroupBy : public Operator<GroupBy<Selector>> {
} }
return true; 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 { Gen compose(GenImpl<Value, Source>&& source) const {
return Gen(std::move(source.self()), selector_); 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 { Gen compose(const GenImpl<Value, Source>& source) const {
return Gen(source.self(), selector_); return Gen(source.self(), selector_);
} }
...@@ -1079,17 +1140,17 @@ class GroupBy : public Operator<GroupBy<Selector>> { ...@@ -1079,17 +1140,17 @@ class GroupBy : public Operator<GroupBy<Selector>> {
* auto c = from(vector) | assert_type<int&>() | sum; * auto c = from(vector) | assert_type<int&>() | sum;
* *
*/ */
template<class Expected> template <class Expected>
class TypeAssertion : public Operator<TypeAssertion<Expected>> { class TypeAssertion : public Operator<TypeAssertion<Expected>> {
public: public:
template<class Source, class Value> template <class Source, class Value>
const Source& compose(const GenImpl<Value, Source>& source) const { const Source& compose(const GenImpl<Value, Source>& source) const {
static_assert(std::is_same<Expected, Value>::value, static_assert(std::is_same<Expected, Value>::value,
"assert_type() check failed"); "assert_type() check failed");
return source.self(); return source.self();
} }
template<class Source, class Value> template <class Source, class Value>
Source&& compose(GenImpl<Value, Source>&& source) const { Source&& compose(GenImpl<Value, Source>&& source) const {
static_assert(std::is_same<Expected, Value>::value, static_assert(std::is_same<Expected, Value>::value,
"assert_type() check failed"); "assert_type() check failed");
...@@ -1109,18 +1170,16 @@ class TypeAssertion : public Operator<TypeAssertion<Expected>> { ...@@ -1109,18 +1170,16 @@ class TypeAssertion : public Operator<TypeAssertion<Expected>> {
* }) * })
* | take(10); * | take(10);
*/ */
template<class Selector> template <class Selector>
class Distinct : public Operator<Distinct<Selector>> { class Distinct : public Operator<Distinct<Selector>> {
Selector selector_; Selector selector_;
public: public:
Distinct() = default; Distinct() = default;
explicit Distinct(Selector selector) explicit Distinct(Selector selector) : selector_(std::move(selector)) {}
: selector_(std::move(selector))
{}
template<class Value, template <class Value, class Source>
class Source>
class Generator : public GenImpl<Value, Generator<Value, Source>> { class Generator : public GenImpl<Value, Generator<Value, Source>> {
Source source_; Source source_;
Selector selector_; Selector selector_;
...@@ -1135,12 +1194,10 @@ class Distinct : public Operator<Distinct<Selector>> { ...@@ -1135,12 +1194,10 @@ class Distinct : public Operator<Distinct<Selector>> {
typedef typename std::decay<KeyType>::type KeyStorageType; typedef typename std::decay<KeyType>::type KeyStorageType;
public: public:
Generator(Source source, Generator(Source source, Selector selector)
Selector selector) : source_(std::move(source)), selector_(std::move(selector)) {}
: source_(std::move(source)),
selector_(std::move(selector)) {}
template<class Body> template <class Body>
void foreach(Body&& body) const { void foreach(Body&& body) const {
std::unordered_set<KeyStorageType> keysSeen; std::unordered_set<KeyStorageType> keysSeen;
source_.foreach([&](Value value) { source_.foreach([&](Value value) {
...@@ -1150,7 +1207,7 @@ class Distinct : public Operator<Distinct<Selector>> { ...@@ -1150,7 +1207,7 @@ class Distinct : public Operator<Distinct<Selector>> {
}); });
} }
template<class Handler> template <class Handler>
bool apply(Handler&& handler) const { bool apply(Handler&& handler) const {
std::unordered_set<KeyStorageType> keysSeen; std::unordered_set<KeyStorageType> keysSeen;
return source_.apply([&](Value value) -> bool { return source_.apply([&](Value value) -> bool {
...@@ -1160,16 +1217,20 @@ class Distinct : public Operator<Distinct<Selector>> { ...@@ -1160,16 +1217,20 @@ class Distinct : public Operator<Distinct<Selector>> {
return true; 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 Value,
class Gen = Generator<Value, Source>> class Gen = Generator<Value, Source>>
Gen compose(GenImpl<Value, Source>&& source) const { Gen compose(GenImpl<Value, Source>&& source) const {
return Gen(std::move(source.self()), selector_); return Gen(std::move(source.self()), selector_);
} }
template<class Source, template <class Source,
class Value, class Value,
class Gen = Generator<Value, Source>> class Gen = Generator<Value, Source>>
Gen compose(const GenImpl<Value, Source>& source) const { Gen compose(const GenImpl<Value, Source>& source) const {
...@@ -1181,16 +1242,16 @@ class Distinct : public Operator<Distinct<Selector>> { ...@@ -1181,16 +1242,16 @@ class Distinct : public Operator<Distinct<Selector>> {
* Composer - Helper class for adapting pipelines into functors. Primarily used * Composer - Helper class for adapting pipelines into functors. Primarily used
* for 'mapOp'. * for 'mapOp'.
*/ */
template<class Operators> template <class Operators>
class Composer { class Composer {
Operators op_; Operators op_;
public: public:
explicit Composer(Operators op) explicit Composer(Operators op) : op_(std::move(op)) {}
: op_(std::move(op)) {}
template<class Source, template <class Source,
class Ret = decltype(std::declval<Operators>() class Ret = decltype(
.compose(std::declval<Source>()))> std::declval<Operators>().compose(std::declval<Source>()))>
Ret operator()(Source&& source) const { Ret operator()(Source&& source) const {
return op_.compose(std::forward<Source>(source)); return op_.compose(std::forward<Source>(source));
} }
...@@ -1211,29 +1272,29 @@ class Composer { ...@@ -1211,29 +1272,29 @@ class Composer {
*/ */
class Batch : public Operator<Batch> { class Batch : public Operator<Batch> {
size_t batchSize_; size_t batchSize_;
public: public:
explicit Batch(size_t batchSize) explicit Batch(size_t batchSize) : batchSize_(batchSize) {
: batchSize_(batchSize) {
if (batchSize_ == 0) { if (batchSize_ == 0) {
throw std::invalid_argument("Batch size must be non-zero!"); throw std::invalid_argument("Batch size must be non-zero!");
} }
} }
template<class Value, template <class Value,
class Source, class Source,
class StorageType = typename std::decay<Value>::type, class StorageType = typename std::decay<Value>::type,
class VectorType = std::vector<StorageType>> class VectorType = std::vector<StorageType>>
class Generator : class Generator
public GenImpl<VectorType&, : public GenImpl<VectorType&,
Generator<Value, Source, StorageType, VectorType>> { Generator<Value, Source, StorageType, VectorType>> {
Source source_; Source source_;
size_t batchSize_; size_t batchSize_;
public: public:
explicit Generator(Source source, size_t batchSize) explicit Generator(Source source, size_t batchSize)
: source_(std::move(source)) : source_(std::move(source)), batchSize_(batchSize) {}
, batchSize_(batchSize) {}
template<class Handler> template <class Handler>
bool apply(Handler&& handler) const { bool apply(Handler&& handler) const {
VectorType batch_; VectorType batch_;
batch_.reserve(batchSize_); batch_.reserve(batchSize_);
...@@ -1255,571 +1316,342 @@ class Batch : public Operator<Batch> { ...@@ -1255,571 +1316,342 @@ class Batch : public Operator<Batch> {
return shouldContinue; return shouldContinue;
} }
// Taking n-tuples of an infinite source is still infinite
static constexpr bool infinite = Source::infinite; static constexpr bool infinite = Source::infinite;
}; };
template<class Source, template <class Source,
class Value, class Value,
class Gen = Generator<Value, Source>> class Gen = Generator<Value, Source>>
Gen compose(GenImpl<Value, Source>&& source) const { Gen compose(GenImpl<Value, Source>&& source) const {
return Gen(std::move(source.self()), batchSize_); return Gen(std::move(source.self()), batchSize_);
} }
template<class Source, template <class Source,
class Value, class Value,
class Gen = Generator<Value, Source>> class Gen = Generator<Value, Source>>
Gen compose(const GenImpl<Value, Source>& source) const { Gen compose(const GenImpl<Value, Source>& source) const {
return Gen(source.self(), batchSize_); return Gen(source.self(), batchSize_);
} }
}; };
/*
* Sinks
*/
/** /**
* FoldLeft - Left-associative functional fold. For producing an aggregate value * Concat - For flattening generators of generators.
* 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: * This type is usually used through the 'concat' static value, like:
* *
* double movingAverage = from(values) * auto edges =
* | foldl(0.0, [](double avg, double sample) { * from(nodes)
* return sample * 0.1 + avg * 0.9; * | map([](Node& x) {
* return from(x.neighbors)
* | map([&](Node& y) {
* return Edge(x, y);
* }); * });
* })
* | concat
* | as<std::set>();
*/ */
template<class Seed, class Concat : public Operator<Concat> {
class Fold>
class FoldLeft : public Operator<FoldLeft<Seed, Fold>> {
Seed seed_;
Fold fold_;
public: public:
FoldLeft() = default; Concat() = default;
FoldLeft(Seed seed,
Fold fold) template <class Inner,
: seed_(std::move(seed)) class Source,
, fold_(std::move(fold)) class InnerValue = typename std::decay<Inner>::type::ValueType>
{} class Generator
: public GenImpl<InnerValue, Generator<Inner, Source, InnerValue>> {
template<class Source, Source 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: public:
First() = default; explicit Generator(Source source) : source_(std::move(source)) {}
template<class Source, template <class Handler>
class Value, bool apply(Handler&& handler) const {
class StorageType = typename std::decay<Value>::type> return source_.apply([&](Inner inner) -> bool {
StorageType compose(const GenImpl<Value, Source>& source) const { return inner.apply(std::forward<Handler>(handler));
Optional<StorageType> accum; });
source | [&](Value v) -> bool { }
accum = std::forward<Value>(v);
return false; 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 * This type is usually used through the 'rconcat' static value, like:
* called on an infinite source, because then there is only one possible return *
* value. * map<int, vector<int>> adjacency;
* auto sinks =
* from(adjacency)
* | get<1>()
* | rconcat()
* | as<std::set>();
*/ */
template <bool emptyResult> class RangeConcat : public Operator<RangeConcat> {
class IsEmpty : public Operator<IsEmpty<emptyResult>> {
public: public:
IsEmpty() = default; RangeConcat() = default;
template<class Source, template <class Range,
class Value> class Source,
bool compose(const GenImpl<Value, Source>& source) const { class InnerValue = typename ValueTypeOfRange<Range>::RefType>
static_assert(!Source::infinite, class Generator
"Cannot call 'all', 'any', 'isEmpty', or 'notEmpty' on " : public GenImpl<InnerValue, Generator<Range, Source, InnerValue>> {
"infinite source. 'all' and 'isEmpty' will either return " Source source_;
"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);
}
};
/**
* 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: public:
Sum() = default; explicit Generator(Source source) : source_(std::move(source)) {}
template<class Source, template <class Body>
class Value, void foreach(Body&& body) const {
class StorageType = typename std::decay<Value>::type> source_.foreach([&](Range range) {
StorageType compose(const GenImpl<Value, Source>& source) const { for (auto& value : range) {
static_assert(!Source::infinite, "Cannot sum infinite source"); body(value);
return foldl(StorageType(0),
[](StorageType&& accum, Value v) {
return std::move(accum) + std::forward<Value>(v);
}).compose(source);
} }
};
/**
* 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));
}); });
} }
};
/** template <class Handler>
* Min - For a value which minimizes a key, where the key is determined by a bool apply(Handler&& handler) const {
* given selector, and compared by given comparer. return source_.apply([&](Range range) -> bool {
* for (auto& value : range) {
* This type is usually used through the singletone 'min' or through the helper if (!handler(value)) {
* functions 'minBy' and 'maxBy'. return false;
*
* 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);
} }
};
if (!min.hasValue()) {
throw EmptySequence();
} }
return min.value(); return true;
});
} }
};
/** // This is similar to concat, except that the inner iterables all are finite
* Append - For collecting values from a source into a given output container // so the only thing that matters is that the source is infinite.
* by appending. static constexpr bool infinite = Source::infinite;
*
* 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));
}; };
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 Source,
class StorageType = typename std::decay<Value>::type> class Gen = Generator<Value, Source>>
Collection compose(const GenImpl<Value, Source>& source) const { Gen compose(GenImpl<Value, Source>&& source) const {
Collection collection; return Gen(std::move(source.self()));
source | [&](Value v) {
collection.insert(collection.end(), std::forward<Value>(v));
};
return collection;
} }
};
/**
* 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 Source,
class StorageType = typename std::decay<Value>::type, class Gen = Generator<Value, Source>>
class Collection = Container<StorageType, Allocator<StorageType>>> Gen compose(const GenImpl<Value, Source>& source) const {
Collection compose(const GenImpl<Value, Source>& source) const { return Gen(source.self());
Collection collection;
source | [&](Value v) {
collection.insert(collection.end(), std::forward<Value>(v));
};
return collection;
} }
}; };
/** /**
* 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 = * auto indexes
* from(nodes) * = byLine(STDIN_FILENO)
* | map([](Node& x) { * | guard<std::runtime_error>([](std::runtime_error& e,
* return from(x.neighbors) * StringPiece sp) {
* | map([&](Node& y) { * LOG(ERROR) << sp << ": " << e.str();
* return Edge(x, y); * return true; // continue processing subsequent lines
* });
* }) * })
* | concat * | eachTo<int>()
* | as<std::set>(); * | as<vector>();
*/ *
class Concat : public Operator<Concat> { * TODO(tjackson): Rename this back to Guard.
**/
template <class Exception, class ErrorHandler>
class GuardImpl : public Operator<GuardImpl<Exception, ErrorHandler>> {
ErrorHandler handler_;
public: public:
Concat() = default; explicit GuardImpl(ErrorHandler handler) : handler_(std::move(handler)) {}
template<class Inner, template <class Value, class Source>
class Source, class Generator : public GenImpl<Value, Generator<Value, Source>> {
class InnerValue = typename std::decay<Inner>::type::ValueType>
class Generator :
public GenImpl<InnerValue, Generator<Inner, Source, InnerValue>> {
Source source_; Source source_;
ErrorHandler handler_;
public: public:
explicit Generator(Source source) explicit Generator(Source source, ErrorHandler handler)
: source_(std::move(source)) {} : source_(std::move(source)), handler_(std::move(handler)) {}
template<class Handler> template <class Handler>
bool apply(Handler&& handler) const { bool apply(Handler&& handler) const {
return source_.apply([&](Inner inner) -> bool { return source_.apply([&](Value value) -> bool {
return inner.apply(std::forward<Handler>(handler)); 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; static constexpr bool infinite = Source::infinite;
}; };
template<class Value, template <class Value,
class Source, class Source,
class Gen = Generator<Value, Source>> class Gen = Generator<Value, Source>>
Gen compose(GenImpl<Value, Source>&& source) const { 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 Source,
class Gen = Generator<Value, Source>> class Gen = Generator<Value, Source>>
Gen compose(const GenImpl<Value, Source>& source) const { 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 refs = from(ptrs) | dereference;
* auto sinks =
* from(adjacency)
* | get<1>()
* | rconcat()
* | as<std::set>();
*/ */
class RangeConcat : public Operator<RangeConcat> { class Dereference : public Operator<Dereference> {
public: public:
RangeConcat() = default; Dereference() = default;
template<class Range, template <class Value,
class Source, class Source,
class InnerValue = typename ValueTypeOfRange<Range>::RefType> class Result = decltype(*std::declval<Value>())>
class Generator class Generator : public GenImpl<Result, Generator<Value, Source, Result>> {
: public GenImpl<InnerValue, Generator<Range, Source, InnerValue>> {
Source source_; Source source_;
public: public:
explicit Generator(Source source) explicit Generator(Source source) : source_(std::move(source)) {}
: source_(std::move(source)) {}
template<class Body> template <class Body>
void foreach(Body&& body) const { void foreach(Body&& body) const {
source_.foreach([&](Range range) { source_.foreach([&](Value value) {
for (auto& value : range) { if (value) {
body(value); return body(*value);
} }
}); });
} }
template<class Handler> template <class Handler>
bool apply(Handler&& handler) const { bool apply(Handler&& handler) const {
return source_.apply([&](Range range) -> bool { return source_.apply([&](Value value) -> bool {
for (auto& value : range) { if (value) {
if (!handler(value)) { return handler(*value);
return false;
}
} }
return true; return true;
}); });
} }
// Just passes value though, length unaffected
static constexpr bool infinite = Source::infinite;
}; };
template<class Value, template <class Source,
class Source, class Value,
class Gen = Generator<Value, Source>> class Gen = Generator<Value, Source>>
Gen compose(GenImpl<Value, Source>&& source) const { Gen compose(GenImpl<Value, Source>&& source) const {
return Gen(std::move(source.self())); return Gen(std::move(source.self()));
} }
template<class Value, template <class Source,
class Source, class Value,
class Gen = Generator<Value, Source>> class Gen = Generator<Value, Source>>
Gen compose(const GenImpl<Value, Source>& source) const { Gen compose(const GenImpl<Value, Source>& source) const {
return Gen(source.self()); return Gen(source.self());
} }
}; };
/** /**
* GuardImpl - For handling exceptions from downstream computation. Requires the * Indirect - For producing a sequence of the addresses of the values in the
* type of exception to catch, and handler function to invoke in the event of * input.
* 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:
* *
* auto indexes * This type is usually used through the 'indirect' static value, like:
* = 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>();
* *
* TODO(tjackson): Rename this back to Guard. * auto ptrs = from(refs) | indirect;
**/ */
template<class Exception, class Indirect : public Operator<Indirect> {
class ErrorHandler>
class GuardImpl : public Operator<GuardImpl<Exception, ErrorHandler>> {
ErrorHandler handler_;
public: public:
explicit GuardImpl(ErrorHandler handler) : handler_(std::move(handler)) {} Indirect() = default;
template<class Value, template <class Value,
class Source> class Source,
class Generator : public GenImpl<Value, Generator<Value, Source>> { class Result = typename std::remove_reference<Value>::type*>
class Generator : public GenImpl<Result, Generator<Value, Source, Result>> {
Source source_; Source source_;
ErrorHandler handler_; static_assert(!std::is_rvalue_reference<Value>::value,
"Cannot use indirect on an rvalue");
public: public:
explicit Generator(Source source, explicit Generator(Source source) : source_(std::move(source)) {}
ErrorHandler handler)
: source_(std::move(source)), template <class Body>
handler_(std::move(handler)) {} void foreach(Body&& body) const {
source_.foreach([&](Value value) {
return body(&value);
});
}
template<class Handler> template <class Handler>
bool apply(Handler&& handler) const { bool apply(Handler&& handler) const {
return source_.apply([&](Value value) -> bool { return source_.apply([&](Value value) -> bool {
try { return handler(&value);
handler(std::forward<Value>(value));
return true;
} catch (Exception& e) {
return handler_(e, std::forward<Value>(value));
}
}); });
} }
// Just passes value though, length unaffected
static constexpr bool infinite = Source::infinite; static constexpr bool infinite = Source::infinite;
}; };
template<class Value, template <class Source,
class Source, class Value,
class Gen = Generator<Value, Source>> class Gen = Generator<Value, Source>>
Gen compose(GenImpl<Value, Source>&& source) const { Gen compose(GenImpl<Value, Source>&& source) const {
return Gen(std::move(source.self()), handler_); return Gen(std::move(source.self()));
} }
template<class Value, template <class Source,
class Source, class Value,
class Gen = Generator<Value, Source>> class Gen = Generator<Value, Source>>
Gen compose(const GenImpl<Value, Source>& source) const { 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>> { ...@@ -1843,24 +1675,22 @@ class Cycle : public Operator<Cycle<forever>> {
public: public:
Cycle() = default; Cycle() = default;
explicit Cycle(off_t limit) explicit Cycle(off_t limit) : limit_(limit) {
: limit_(limit) {
static_assert( static_assert(
!forever, !forever,
"Cycle limit consturctor should not be used when forever == true."); "Cycle limit consturctor should not be used when forever == true.");
} }
template<class Value, template <class Value, class Source>
class Source>
class Generator : public GenImpl<Value, Generator<Value, Source>> { class Generator : public GenImpl<Value, Generator<Value, Source>> {
Source source_; Source source_;
off_t limit_; off_t limit_;
public: public:
explicit Generator(Source source, off_t limit) explicit Generator(Source source, off_t limit)
: source_(std::move(source)) : source_(std::move(source)), limit_(limit) {}
, limit_(limit) {}
template<class Handler> template <class Handler>
bool apply(Handler&& handler) const { bool apply(Handler&& handler) const {
bool cont; bool cont;
auto handler2 = [&](Value value) { auto handler2 = [&](Value value) {
...@@ -1875,145 +1705,391 @@ class Cycle : public Operator<Cycle<forever>> { ...@@ -1875,145 +1705,391 @@ class Cycle : public Operator<Cycle<forever>> {
return false; 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, template <class Source, class Value>
class Value, size_t compose(const GenImpl<Value, Source>& source) const {
class Gen = Generator<Value, Source>> static_assert(!Source::infinite, "Cannot count infinite source");
Gen compose(GenImpl<Value, Source>&& source) const { return foldl(size_t(0),
return Gen(std::move(source.self()), limit_); [](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 Value,
class Gen = Generator<Value, Source>> class StorageType = typename std::decay<Value>::type>
Gen compose(const GenImpl<Value, Source>& source) const { StorageType compose(const GenImpl<Value, Source>& source) const {
return Gen(source.self(), limit_); 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 { template <class Needle>
return Cycle<false>(limit); 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 * Min - For a value which minimizes a key, where the key is determined by a
* null pointers. * 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> { template <class Selector, class Comparer>
public: class Min : public Operator<Min<Selector, Comparer>> {
Dereference() = default; 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: public:
explicit Generator(Source source) Min() = default;
: source_(std::move(source)) {}
template<class Body> explicit Min(Selector selector) : selector_(std::move(selector)) {}
void foreach(Body&& body) const {
source_.foreach([&](Value value) {
if (value) {
return body(*value);
}
});
}
template<class Handler> Min(Selector selector, Comparer comparer)
bool apply(Handler&& handler) const { : selector_(std::move(selector)), comparer_(std::move(comparer)) {}
return source_.apply([&](Value value) -> bool {
if (value) { template <class Value,
return handler(*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, public:
class Value, explicit Append(Collection* collection) : collection_(collection) {}
class Gen = Generator<Value, Source>>
Gen compose(GenImpl<Value, Source>&& source) const {
return Gen(std::move(source.self()));
}
template<class Source, template <class Value, class Source>
class Value, Collection& compose(const GenImpl<Value, Source>& source) const {
class Gen = Generator<Value, Source>> static_assert(!Source::infinite, "Cannot appendTo with infinite source");
Gen compose(const GenImpl<Value, Source>& source) const { source | [&](Value v) {
return Gen(source.self()); collection_->insert(collection_->end(), std::forward<Value>(v));
};
return *collection_;
} }
}; };
/** /**
* Indirect - For producing a sequence of the addresses of the values in the * Collect - For collecting values from a source in a collection of the desired
* input. * 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: public:
Indirect() = default; Collect() = default;
template <class Value, template <class Value,
class Source, class Source,
class Result = typename std::remove_reference<Value>::type*> class StorageType = typename std::decay<Value>::type>
class Generator : public GenImpl<Result, Generator<Value, Source, Result>> { Collection compose(const GenImpl<Value, Source>& source) const {
Source source_; static_assert(!Source::infinite,
static_assert(!std::is_rvalue_reference<Value>::value, "Cannot convert infinite source to object with as.");
"Cannot use indirect on an rvalue"); Collection collection;
source | [&](Value v) {
public: collection.insert(collection.end(), std::forward<Value>(v));
explicit Generator(Source source) : source_(std::move(source)) {} };
return collection;
template <class Body>
void foreach(Body&& body) const {
source_.foreach([&](Value value) {
return body(&value);
});
} }
};
template <class Handler> /**
bool apply(Handler&& handler) const { * CollectTemplate - For collecting values from a source in a collection
return source_.apply([&](Value value) -> bool { * constructed using the specified template type. Given the type of values
return handler(&value); * 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. template <class Value,
static constexpr bool infinite = Source::infinite; 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));
}; };
return collection;
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());
} }
}; };
...@@ -2022,7 +2098,7 @@ class Indirect : public Operator<Indirect> { ...@@ -2022,7 +2098,7 @@ class Indirect : public Operator<Indirect> {
/** /**
* VirtualGen<T> - For wrapping template types in simple polymorphic wrapper. * VirtualGen<T> - For wrapping template types in simple polymorphic wrapper.
**/ **/
template<class Value> template <class Value>
class VirtualGen : public GenImpl<Value, VirtualGen<Value>> { class VirtualGen : public GenImpl<Value, VirtualGen<Value>> {
class WrapperBase { class WrapperBase {
public: public:
...@@ -2032,13 +2108,12 @@ class VirtualGen : public GenImpl<Value, VirtualGen<Value>> { ...@@ -2032,13 +2108,12 @@ class VirtualGen : public GenImpl<Value, VirtualGen<Value>> {
virtual std::unique_ptr<const WrapperBase> clone() const = 0; virtual std::unique_ptr<const WrapperBase> clone() const = 0;
}; };
template<class Wrapped> template <class Wrapped>
class WrapperImpl : public WrapperBase { class WrapperImpl : public WrapperBase {
Wrapped wrapped_; Wrapped wrapped_;
public: public:
explicit WrapperImpl(Wrapped wrapped) explicit WrapperImpl(Wrapped wrapped) : wrapped_(std::move(wrapped)) {}
: wrapped_(std::move(wrapped)) {
}
virtual bool apply(const std::function<bool(Value)>& handler) const { virtual bool apply(const std::function<bool(Value)>& handler) const {
return wrapped_.apply(handler); return wrapped_.apply(handler);
...@@ -2063,8 +2138,7 @@ class VirtualGen : public GenImpl<Value, VirtualGen<Value>> { ...@@ -2063,8 +2138,7 @@ class VirtualGen : public GenImpl<Value, VirtualGen<Value>> {
VirtualGen(VirtualGen&& source) noexcept VirtualGen(VirtualGen&& source) noexcept
: wrapper_(std::move(source.wrapper_)) {} : wrapper_(std::move(source.wrapper_)) {}
VirtualGen(const VirtualGen& source) VirtualGen(const VirtualGen& source) : wrapper_(source.wrapper_->clone()) {}
: wrapper_(source.wrapper_->clone()) {}
VirtualGen& operator=(const VirtualGen& source) { VirtualGen& operator=(const VirtualGen& source) {
wrapper_.reset(source.wrapper_->clone()); wrapper_.reset(source.wrapper_->clone());
...@@ -2072,7 +2146,7 @@ class VirtualGen : public GenImpl<Value, VirtualGen<Value>> { ...@@ -2072,7 +2146,7 @@ class VirtualGen : public GenImpl<Value, VirtualGen<Value>> {
} }
VirtualGen& operator=(VirtualGen&& source) noexcept { VirtualGen& operator=(VirtualGen&& source) noexcept {
wrapper_= std::move(source.wrapper_); wrapper_ = std::move(source.wrapper_);
return *this; return *this;
} }
...@@ -2095,12 +2169,6 @@ constexpr detail::Count count{}; ...@@ -2095,12 +2169,6 @@ constexpr detail::Count count{};
constexpr detail::First first{}; 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<true> isEmpty{};
constexpr detail::IsEmpty<false> notEmpty{}; constexpr detail::IsEmpty<false> notEmpty{};
...@@ -2125,27 +2193,21 @@ constexpr detail::Dereference dereference{}; ...@@ -2125,27 +2193,21 @@ constexpr detail::Dereference dereference{};
constexpr detail::Indirect indirect{}; constexpr detail::Indirect indirect{};
inline detail::Take take(size_t count) { inline detail::Take take(size_t count) { return detail::Take(count); }
return detail::Take(count);
}
inline detail::Stride stride(size_t s) { inline detail::Stride stride(size_t s) { return detail::Stride(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()) { inline detail::Sample<Random> sample(size_t count, Random rng = Random()) {
return detail::Sample<Random>(count, std::move(rng)); return detail::Sample<Random>(count, std::move(rng));
} }
inline detail::Skip skip(size_t count) { inline detail::Skip skip(size_t count) { return detail::Skip(count); }
return detail::Skip(count);
}
inline detail::Batch batch(size_t batchSize) { inline detail::Batch batch(size_t batchSize) {
return detail::Batch(batchSize); return detail::Batch(batchSize);
} }
}} //folly::gen }} // folly::gen
#pragma GCC diagnostic pop #pragma GCC diagnostic pop
...@@ -181,6 +181,13 @@ class GenImpl : public FBounded<Self> { ...@@ -181,6 +181,13 @@ class GenImpl : public FBounded<Self> {
// Child classes should override if the sequence generated is *definitely* // Child classes should override if the sequence generated is *definitely*
// infinite. 'infinite' may be false_type for some infinite sequences // infinite. 'infinite' may be false_type for some infinite sequences
// (due the the Halting Problem). // (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; static constexpr bool infinite = false;
}; };
......
...@@ -577,6 +577,37 @@ TEST(Gen, DistinctMove) { // 0 1 4 9 6 5 6 9 4 1 0 ...@@ -577,6 +577,37 @@ TEST(Gen, DistinctMove) { // 0 1 4 9 6 5 6 9 4 1 0
EXPECT_EQ(expected, actual); 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) { TEST(Gen, MinBy) {
EXPECT_EQ(7, seq(1, 10) EXPECT_EQ(7, seq(1, 10)
| minBy([](int i) -> double { | minBy([](int i) -> double {
...@@ -743,21 +774,17 @@ TEST(Gen, Get) { ...@@ -743,21 +774,17 @@ TEST(Gen, Get) {
} }
TEST(Gen, notEmpty) { TEST(Gen, notEmpty) {
EXPECT_TRUE(seq(0) | notEmpty);
EXPECT_TRUE(seq(0, 1) | notEmpty); EXPECT_TRUE(seq(0, 1) | notEmpty);
EXPECT_TRUE(just(1) | notEmpty); EXPECT_TRUE(just(1) | notEmpty);
EXPECT_FALSE(gen::range(0, 0) | notEmpty); EXPECT_FALSE(gen::range(0, 0) | notEmpty);
EXPECT_FALSE(from({1}) | take(0) | notEmpty); EXPECT_FALSE(from({1}) | take(0) | notEmpty);
EXPECT_TRUE(seq(1, 3) | cycle | notEmpty);
} }
TEST(Gen, isEmpty) { TEST(Gen, isEmpty) {
EXPECT_FALSE(seq(0) | isEmpty);
EXPECT_FALSE(seq(0, 1) | isEmpty); EXPECT_FALSE(seq(0, 1) | isEmpty);
EXPECT_FALSE(just(1) | isEmpty); EXPECT_FALSE(just(1) | isEmpty);
EXPECT_TRUE(gen::range(0, 0) | isEmpty); EXPECT_TRUE(gen::range(0, 0) | isEmpty);
EXPECT_TRUE(from({1}) | take(0) | isEmpty); EXPECT_TRUE(from({1}) | take(0) | isEmpty);
EXPECT_FALSE(seq(1, 3) | cycle | isEmpty);
} }
TEST(Gen, Any) { TEST(Gen, Any) {
...@@ -1018,7 +1045,8 @@ TEST(Gen, Cycle) { ...@@ -1018,7 +1045,8 @@ TEST(Gen, Cycle) {
}; };
auto s = countdown; auto s = countdown;
EXPECT_EQ((vector<int> { 1, 2, 3, 1, 2, 1}), 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