Cycle
authorTom Jackson <tjackson@fb.com>
Thu, 1 Aug 2013 20:54:24 +0000 (13:54 -0700)
committerSara Golemon <sgolemon@fb.com>
Wed, 28 Aug 2013 21:30:11 +0000 (14:30 -0700)
Summary: For repeating a generator's values endlessly.

Test Plan: Unit tests

Reviewed By: kittipat@fb.com

FB internal diff: D911178

folly/experimental/Gen-inl.h
folly/experimental/Gen.h
folly/experimental/test/GenTest.cpp

index f553cc869cafaa2afd9daf03964089f2c665ccfd..9a7d568f8d9ae9be102dc195c3724e8cec134af3 100644 (file)
@@ -1794,6 +1794,81 @@ class GuardImpl : public Operator<GuardImpl<Exception, ErrorHandler>> {
     return Gen(source.self(), handler_);
   }
 };
+
+/**
+ * Cycle - For repeating a sequence forever.
+ *
+ * This type is usually used through the 'cycle' static value, like:
+ *
+ *   auto tests
+ *     = from(samples)
+ *     | cycle
+ *     | take(100);
+ */
+class Cycle : public Operator<Cycle> {
+  off_t limit_; // -1 for infinite
+ public:
+  Cycle()
+    : limit_(-1) { }
+
+  explicit Cycle(off_t limit)
+    : limit_(limit) { }
+
+  template<class Value,
+           class Source>
+  class Generator : public GenImpl<Value, Generator<Value, Source>> {
+    Source source_;
+    off_t limit_; // -1 for infinite
+  public:
+    explicit Generator(Source source, off_t limit)
+      : source_(std::move(source))
+      , limit_(limit) {}
+
+    template<class Handler>
+    bool apply(Handler&& handler) const {
+      bool cont;
+      auto handler2 = [&](Value value) {
+        cont = handler(std::forward<Value>(value));
+        return cont;
+      };
+      for (off_t count = 0; count != limit_; ++count) {
+        cont = false;
+        source_.apply(handler2);
+        if (!cont) {
+          return false;
+        }
+      }
+      return true;
+    }
+
+    // not actually infinite, since an empty generator will end the cycles.
+    static constexpr bool infinite = 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 use like:
+   *
+   *  auto tripled = gen | cycle(3);
+   */
+  Cycle operator()(off_t limit) const {
+    return Cycle(limit);
+  }
+};
+
 } //::detail
 
 /**
@@ -1875,6 +1950,13 @@ static const detail::Count count;
 
 static const detail::First first;
 
+/**
+ * Use directly for detecting any values, or as a function to detect values
+ * which pass a predicate:
+ *
+ *  auto nonempty = g | any;
+ *  auto evens = g | any(even);
+ */
 static const detail::Any any;
 
 static const detail::Min<Identity, Less> min;
@@ -1891,6 +1973,14 @@ static const detail::Concat concat;
 
 static const detail::RangeConcat rconcat;
 
+/**
+ * Use directly for infinite sequences, or as a function to limit cycle count.
+ *
+ *  auto forever = g | cycle;
+ *  auto thrice = g | cycle(3);
+ */
+static const detail::Cycle cycle;
+
 inline detail::Take take(size_t count) {
   return detail::Take(count);
 }
index 342968b0297d9583cacc279cc118537d741dcea4..31c1cedc219a88b0415b1e14015161ee145f25fe 100644 (file)
@@ -304,6 +304,12 @@ class Composed;
 template<class Expected>
 class TypeAssertion;
 
+class Concat;
+
+class RangeConcat;
+
+class Cycle;
+
 /*
  * Sinks
  */
index d2412af0e85ef030a6f65e882be8cff17a685967..9ab1aa57a8f561323cd8448022dc586348b1accc 100644 (file)
@@ -948,6 +948,44 @@ TEST(StringGen, EmptySplit) {
   }
 }
 
+TEST(Gen, Cycle) {
+  {
+    auto s = from({1, 2});
+    EXPECT_EQ((vector<int> { 1, 2, 1, 2, 1 }),
+              s | cycle | take(5) | as<vector>());
+  }
+  {
+    auto s = from({1, 2});
+    EXPECT_EQ((vector<int> { 1, 2, 1, 2 }),
+              s | cycle(2) | as<vector>());
+  }
+  {
+    auto s = from({1, 2, 3});
+    EXPECT_EQ((vector<int> { 1, 2, 1, 2, 1 }),
+              s | take(2) | cycle | take(5) | as<vector>());
+  }
+  {
+    auto s = empty<int>();
+    EXPECT_EQ((vector<int> { }),
+              s | cycle | take(4) | as<vector>());
+  }
+  {
+    int count = 3;
+    int* pcount = &count;
+    auto countdown = GENERATOR(int) {
+      ASSERT_GE(*pcount, 0)
+        << "Cycle should have stopped when it didnt' get values!";
+      for (int i = 1; i <= *pcount; ++i) {
+        yield(i);
+      }
+      --*pcount;
+    };
+    auto s = countdown;
+    EXPECT_EQ((vector<int> { 1, 2, 3, 1, 2, 1}),
+              s | cycle | as<vector>());
+  }
+}
+
 TEST(StringGen, Split) {
   auto collect = eachTo<std::string>() | as<vector>();
   {
@@ -1099,7 +1137,6 @@ TEST(StringGen, EachToPair) {
   }
 }
 
-
 TEST(StringGen, Resplit) {
   auto collect = eachTo<std::string>() | as<vector>();
   {