stride()
authorTom Jackson <tjackson@fb.com>
Thu, 3 Jul 2014 22:28:27 +0000 (15:28 -0700)
committerTudor Bosman <tudorb@fb.com>
Wed, 9 Jul 2014 20:52:02 +0000 (13:52 -0700)
Summary: thatwaseasy

Test Plan: iloveunittests

Reviewed By: lucian@fb.com

Subscribers: philipp

FB internal diff: D1419848

Tasks: 4636617

folly/gen/Base-inl.h
folly/gen/Base.h
folly/gen/test/BaseTest.cpp

index a805b08e9dea802d1b79ab63173e0ac822f6f386..faaa92ac6d4040cc82f434bb2e2d251d7ae0087e 100644 (file)
@@ -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));
index 047de1e923ba8571ed05d7ffe86233b6998a0a97..2c8c2635f32a5e265206b1dc4939bdb7f89e318b 100644 (file)
@@ -313,6 +313,8 @@ class Until;
 
 class Take;
 
+class Stride;
+
 template<class Rand>
 class Sample;
 
index 7d34a21db1b2fa35acc88baed2992bdb335e4267..2ff11c83eb7feb580692108027c5d2d316abe76a 100644 (file)
@@ -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;
   }