Commit 2015ae71 authored by Tom Jackson's avatar Tom Jackson Committed by Tudor Bosman

stride()

Summary: thatwaseasy

Test Plan: iloveunittests

Reviewed By: lucian@fb.com

Subscribers: philipp

FB internal diff: D1419848

Tasks: 4636617
parent db66e2f4
......@@ -334,7 +334,7 @@ class Just : public GenImpl<const Value&, Just<Value>> {
"Just requires non-ref types");
const Value value_;
public:
Just(Value value) : value_(std::forward<Value>(value)) {}
explicit Just(Value value) : value_(std::forward<Value>(value)) {}
template <class Handler>
bool apply(Handler&& handler) const {
......@@ -598,6 +598,69 @@ class Take : public Operator<Take> {
}
};
/**
* Stride - For producing every Nth value from a source.
*
* This type is usually used through the 'stride' helper function, like:
*
* auto half = from(samples)
* | stride(2);
*/
class Stride : public Operator<Stride> {
size_t stride_;
public:
explicit Stride(size_t stride) : stride_(stride) {
if (stride == 0) {
throw std::invalid_argument("stride must not be 0");
}
}
template <class Value, class Source>
class Generator : public GenImpl<Value, Generator<Value, Source>> {
Source source_;
size_t stride_;
public:
Generator(Source source, size_t stride)
: source_(std::move(source)), stride_(stride) {}
template <class Handler>
bool apply(Handler&& handler) const {
size_t distance = stride_;
return source_.apply([&](Value value)->bool {
if (++distance >= stride_) {
if (!handler(std::forward<Value>(value))) {
return false;
}
distance = 0;
}
return true;
});
}
template <class Body>
void foreach(Body&& body) const {
size_t distance = stride_;
source_.foreach([&](Value value) {
if (++distance >= stride_) {
body(std::forward<Value>(value));
distance = 0;
}
});
}
};
template <class Source, class Value, class Gen = Generator<Value, Source>>
Gen compose(GenImpl<Value, Source>&& source) const {
return Gen(std::move(source.self()), stride_);
}
template <class Source, class Value, class Gen = Generator<Value, Source>>
Gen compose(const GenImpl<Value, Source>& source) const {
return Gen(source.self(), stride_);
}
};
/**
* Sample - For taking a random sample of N elements from a sequence
* (without replacement).
......@@ -1615,8 +1678,7 @@ template<class Exception,
class GuardImpl : public Operator<GuardImpl<Exception, ErrorHandler>> {
ErrorHandler handler_;
public:
GuardImpl(ErrorHandler handler)
: handler_(std::move(handler)) {}
explicit GuardImpl(ErrorHandler handler) : handler_(std::move(handler)) {}
template<class Value,
class Source>
......@@ -1801,7 +1863,7 @@ template<class Value>
class VirtualGen : public GenImpl<Value, VirtualGen<Value>> {
class WrapperBase {
public:
virtual ~WrapperBase() {}
virtual ~WrapperBase() noexcept {}
virtual bool apply(const std::function<bool(Value)>& handler) const = 0;
virtual void foreach(const std::function<void(Value)>& body) const = 0;
virtual std::unique_ptr<const WrapperBase> clone() const = 0;
......@@ -1831,25 +1893,22 @@ class VirtualGen : public GenImpl<Value, VirtualGen<Value>> {
std::unique_ptr<const WrapperBase> wrapper_;
public:
template<class Self>
template <class Self>
/* implicit */ VirtualGen(Self source)
: wrapper_(new WrapperImpl<Self>(std::move(source)))
{ }
: wrapper_(new WrapperImpl<Self>(std::move(source))) {}
VirtualGen(VirtualGen&& source)
: wrapper_(std::move(source.wrapper_))
{ }
VirtualGen(VirtualGen&& source) noexcept
: wrapper_(std::move(source.wrapper_)) {}
VirtualGen(const VirtualGen& source)
: wrapper_(source.wrapper_->clone())
{ }
: wrapper_(source.wrapper_->clone()) {}
VirtualGen& operator=(const VirtualGen& source) {
wrapper_.reset(source.wrapper_->clone());
return *this;
}
VirtualGen& operator=(VirtualGen&& source) {
VirtualGen& operator=(VirtualGen&& source) noexcept {
wrapper_= std::move(source.wrapper_);
return *this;
}
......@@ -1910,6 +1969,10 @@ inline detail::Take take(size_t count) {
return detail::Take(count);
}
inline detail::Stride stride(size_t s) {
return detail::Stride(s);
}
template<class Random = std::default_random_engine>
inline detail::Sample<Random> sample(size_t count, Random rng = Random()) {
return detail::Sample<Random>(count, std::move(rng));
......
......@@ -313,6 +313,8 @@ class Until;
class Take;
class Stride;
template<class Rand>
class Sample;
......
......@@ -308,6 +308,47 @@ TEST(Gen, Take) {
}
}
TEST(Gen, Stride) {
{
EXPECT_THROW(stride(0), std::invalid_argument);
}
{
auto expected = vector<int>{1, 2, 3, 4};
auto actual
= seq(1, 4)
| stride(1)
| as<vector<int>>();
EXPECT_EQ(expected, actual);
}
{
auto expected = vector<int>{1, 3, 5, 7};
auto actual
= seq(1, 8)
| stride(2)
| as<vector<int>>();
EXPECT_EQ(expected, actual);
}
{
auto expected = vector<int>{1, 4, 7, 10};
auto actual
= seq(1, 12)
| stride(3)
| as<vector<int>>();
EXPECT_EQ(expected, actual);
}
{
auto expected = vector<int>{1, 3, 5, 7, 9, 1, 4, 7, 10};
auto actual
= ((seq(1, 10) | stride(2)) +
(seq(1, 10) | stride(3)))
| as<vector<int>>();
EXPECT_EQ(expected, actual);
}
EXPECT_EQ(500, seq(1) | take(1000) | stride(2) | count);
EXPECT_EQ(10, seq(1) | take(1000) | stride(2) | take(10) | count);
}
TEST(Gen, Sample) {
std::mt19937 rnd(42);
......@@ -767,8 +808,8 @@ class TestIntSeq : public GenImpl<int, TestIntSeq> {
return true;
}
TestIntSeq(TestIntSeq&&) = default;
TestIntSeq& operator=(TestIntSeq&&) = default;
TestIntSeq(TestIntSeq&&) noexcept = default;
TestIntSeq& operator=(TestIntSeq&&) noexcept = default;
TestIntSeq(const TestIntSeq&) = delete;
TestIntSeq& operator=(const TestIntSeq&) = delete;
};
......@@ -809,7 +850,7 @@ struct CopyCounter {
++alive;
}
CopyCounter(CopyCounter&& source) {
CopyCounter(CopyCounter&& source) noexcept {
*this = std::move(source);
++alive;
}
......
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