(Folly/Gen) Make ranges and sequences take a stepping functor
authorHannes Roth <hannesr@fb.com>
Thu, 17 Apr 2014 16:26:56 +0000 (09:26 -0700)
committerSara Golemon <sgolemon@fb.com>
Fri, 18 Apr 2014 19:04:15 +0000 (12:04 -0700)
Summary:
The functor allows to create ranges and sequences that advance the
iterator by more than 1 per iteration.

Test Plan: Unit test.

Reviewed By: tjackson@fb.com

FB internal diff: D1228065

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

index 1c2da985d2ee06b155fa26bb8a965ac77becb2df..a805b08e9dea802d1b79ab63173e0ac822f6f386 100644 (file)
@@ -183,47 +183,29 @@ class RangeSource : public GenImpl<typename Range<Iterator>::reference,
 
 /**
  * Sequence - For generating values from beginning value, incremented along the
- * way with the ++ and += operators. Iteration may continue indefinitely by
- * setting the 'endless' template parameter to true. If set to false, iteration
- * will stop when value reaches 'end', either inclusively or exclusively,
- * depending on the template parameter 'endInclusive'. Value type specified
- * explicitly.
+ * way with the ++ and += operators. Iteration may continue indefinitely.
+ * Value type specified explicitly.
  *
  * This type is primarily used through the 'seq' and 'range' function, like:
  *
  *   int total = seq(1, 10) | sum;
  *   auto indexes = range(0, 10);
+ *   auto endless = seq(0); // 0, 1, 2, 3, ...
  */
-template<class Value,
-         bool endless,
-         bool endInclusive>
-class Sequence : public GenImpl<const Value&,
-                                Sequence<Value, endless, endInclusive>> {
+template<class Value, class SequenceImpl>
+class Sequence : public GenImpl<const Value&, Sequence<Value, SequenceImpl>> {
   static_assert(!std::is_reference<Value>::value &&
                 !std::is_const<Value>::value, "Value mustn't be const or ref.");
-  Value bounds_[endless ? 1 : 2];
+  Value start_;
+  SequenceImpl impl_;
 public:
-  explicit Sequence(Value begin)
-      : bounds_{std::move(begin)} {
-    static_assert(endless, "Must supply 'end'");
-  }
-
-  Sequence(Value begin,
-           Value end)
-    : bounds_{std::move(begin), std::move(end)} {}
+  explicit Sequence(Value start, SequenceImpl impl)
+      : start_(std::move(start)), impl_(std::move(impl)) { }
 
   template<class Handler>
   bool apply(Handler&& handler) const {
-    Value value = bounds_[0];
-    for (;endless || value < bounds_[1]; ++value) {
-      const Value& arg = value;
-      if (!handler(arg)) {
-        return false;
-      }
-    }
-    if (endInclusive && value == bounds_[1]) {
-      const Value& arg = value;
-      if (!handler(arg)) {
+    for (Value current = start_; impl_.test(current); impl_.step(current)) {
+      if (!handler(current)) {
         return false;
       }
     }
@@ -232,18 +214,60 @@ public:
 
   template<class Body>
   void foreach(Body&& body) const {
-    Value value = bounds_[0];
-    for (;endless || value < bounds_[1]; ++value) {
-      const Value& arg = value;
-      body(arg);
-    }
-    if (endInclusive && value == bounds_[1]) {
-      const Value& arg = value;
-      body(arg);
+    for (Value current = start_; impl_.test(current); impl_.step(current)) {
+      body(current);
     }
   }
+};
+
+/**
+ * Sequence implementations (range, sequence, infinite, with/without step)
+ **/
+template<class Value>
+class RangeImpl {
+  Value end_;
+ public:
+  explicit RangeImpl(Value end) : end_(std::move(end)) { }
+  bool test(const Value& current) const { return current < end_; }
+  void step(Value& current) const { ++current; }
+};
 
-  static constexpr bool infinite = endless;
+template<class Value, class Distance>
+class RangeWithStepImpl {
+  Value end_;
+  Distance step_;
+ public:
+  explicit RangeWithStepImpl(Value end, Distance step)
+    : end_(std::move(end)), step_(std::move(step)) { }
+  bool test(const Value& current) const { return current < end_; }
+  void step(Value& current) const { current += step_; }
+};
+
+template<class Value>
+class SeqImpl {
+  Value end_;
+ public:
+  explicit SeqImpl(Value end) : end_(std::move(end)) { }
+  bool test(const Value& current) const { return current <= end_; }
+  void step(Value& current) const { ++current; }
+};
+
+template<class Value, class Distance>
+class SeqWithStepImpl {
+  Value end_;
+  Distance step_;
+ public:
+  explicit SeqWithStepImpl(Value end, Distance step)
+    : end_(std::move(end)), step_(std::move(step)) { }
+  bool test(const Value& current) const { return current <= end_; }
+  void step(Value& current) const { current += step_; }
+};
+
+template<class Value>
+class InfiniteImpl {
+ public:
+  bool test(const Value& current) const { return true; }
+  void step(Value& current) const { ++current; }
 };
 
 /**
index 06cc0e3c89e1f786e9987a67b17e8f1b6d25b3a7..287d4a6c441d3a9050b16cabaf8a84d5b455327a 100644 (file)
@@ -256,9 +256,24 @@ template<class Value,
          class Container = std::vector<typename std::decay<Value>::type>>
 class CopiedSource;
 
-template<class Value, bool endless = false, bool endInclusive = false>
+template<class Value, class SequenceImpl>
 class Sequence;
 
+template <class Value>
+class RangeImpl;
+
+template <class Value, class Distance>
+class RangeWithStepImpl;
+
+template <class Value>
+class SeqImpl;
+
+template <class Value, class Distance>
+class SeqWithStepImpl;
+
+template <class Value>
+class InfiniteImpl;
+
 template<class Value, class Source>
 class Yield;
 
@@ -396,21 +411,36 @@ From from(Container&& source) {
   return From(std::move(source));
 }
 
-template<class Value, class Gen = detail::Sequence<Value, false, false>>
+template<class Value, class Impl = detail::RangeImpl<Value>,
+         class Gen = detail::Sequence<Value, Impl>>
 Gen range(Value begin, Value end) {
-  return Gen(begin, end);
+  return Gen{std::move(begin), Impl{std::move(end)}};
 }
 
-template<class Value,
-         class Gen = detail::Sequence<Value, false, true>>
+template<class Value, class Distance,
+         class Impl = detail::RangeWithStepImpl<Value, Distance>,
+         class Gen = detail::Sequence<Value, Impl>>
+Gen range(Value begin, Value end, Distance step) {
+  return Gen{std::move(begin), Impl{std::move(end), std::move(step)}};
+}
+
+template<class Value, class Impl = detail::SeqImpl<Value>,
+         class Gen = detail::Sequence<Value, Impl>>
 Gen seq(Value first, Value last) {
-  return Gen(first, last);
+  return Gen{std::move(first), Impl{std::move(last)}};
 }
 
-template<class Value,
-         class Gen = detail::Sequence<Value, true>>
-Gen seq(Value begin) {
-  return Gen(begin);
+template<class Value, class Distance,
+         class Impl = detail::SeqWithStepImpl<Value, Distance>,
+         class Gen = detail::Sequence<Value, Impl>>
+Gen seq(Value first, Value last, Distance step) {
+  return Gen{std::move(first), Impl{std::move(last), std::move(step)}};
+}
+
+template<class Value, class Impl = detail::InfiniteImpl<Value>,
+         class Gen = detail::Sequence<Value, Impl>>
+Gen seq(Value first) {
+  return Gen{std::move(first), Impl{}};
 }
 
 template<class Value,
index 06e50f376014ef964d2dc08d33f00ee84858e0d1..99644c28be3f0e67b357edbdcca687e8f4bf0246 100644 (file)
@@ -186,6 +186,17 @@ TEST(Gen, Seq) {
   }
 }
 
+TEST(Gen, SeqWithStep) {
+  EXPECT_EQ(75, seq(5, 25, 5) | sum);
+}
+
+TEST(Gen, SeqWithStepArray) {
+  const std::array<int, 6> arr{{1, 2, 3, 4, 5, 6}};
+  EXPECT_EQ(9, seq(&arr[0], &arr[5], 2)
+             | map([](const int *i) { return *i; })
+             | sum);
+}
+
 TEST(Gen, Range) {
   // cover the fenceposts of the loop unrolling
   for (int n = 1; n < 100; ++n) {
@@ -193,6 +204,10 @@ TEST(Gen, Range) {
   }
 }
 
+TEST(Gen, RangeWithStep) {
+  EXPECT_EQ(50, range(5, 25, 5) | sum);
+}
+
 TEST(Gen, FromIterators) {
   vector<int> source {2, 3, 5, 7, 11};
   auto gen = from(folly::range(source.begin() + 1, source.end() - 1));