Prechádzať zdrojové kódy

feat: add min/max/minmax, count/count_if, result types

Sam Jaffe 2 rokov pred
rodič
commit
e81793f61b

+ 0 - 86
include/stream/accumulate.h

@@ -1,86 +0,0 @@
-//
-//  accumulate.h
-//  stream
-//
-//  Created by Sam Jaffe on 3/29/23.
-//
-
-#pragma once
-
-#include <optional>
-
-#include <stream/detail/identity.h>
-#include <stream/detail/traits.h>
-
-#define FWD(x) std::forward<decltype(x)>(x)
-
-namespace stream::ranges {
-template <typename It, typename S, typename T, typename F>
-auto fold_left(It it, S end, T init, F reduce) {
-  for (; it != end; ++it) {
-    init = reduce(init, *it);
-  }
-  return init;
-}
-
-template <typename Stream, typename T, typename F>
-auto fold_left(Stream && stream, T init, F reduce) {
-  for (auto && elem : FWD(stream)) {
-    init = reduce(init, FWD(elem));
-  }
-  return init;
-}
-
-template <typename Stream, typename F>
-auto fold_left_with_first(Stream && stream, F reduce)
-    -> std::optional<traits::value_type<Stream>> {
-  if (stream.empty()) { return std::nullopt; }
-  return fold_left(++stream.begin(), stream.end(), *stream.begin(), reduce);
-}
-
-template <typename Stream>
-auto first(Stream && stream) -> std::optional<traits::value_type<Stream>> {
-  return stream.empty() ? std::nullopt : std::optional(*stream.begin());
-}
-
-template <typename It, typename S, typename Pred,
-          typename Proj = detail::identity>
-bool any_of(It it, S end, Pred pred, Proj proj = {}) {
-  for (; it != end; ++it) {
-    if (pred(proj(*it))) { return true; }
-  }
-  return false;
-}
-
-template <typename Stream, typename Pred, typename Proj = detail::identity>
-bool any_of(Stream const & stream, Pred pred, Proj proj = {}) {
-  return any_of(stream.begin(), stream.end(), pred, proj);
-}
-
-template <typename It, typename S, typename Pred,
-          typename Proj = detail::identity>
-bool none_of(It it, S end, Pred pred, Proj proj = {}) {
-  return !any_of(it, end, pred, proj);
-}
-
-template <typename Stream, typename Pred, typename Proj = detail::identity>
-bool none_of(Stream const & stream, Pred pred, Proj proj = {}) {
-  return none_of(stream.begin(), stream.end(), pred, proj);
-}
-
-template <typename It, typename S, typename Pred,
-          typename Proj = detail::identity>
-bool all_of(It it, S end, Pred pred, Proj proj = {}) {
-  for (; it != end; ++it) {
-    if (!pred(proj(*it))) { return false; }
-  }
-  return true;
-}
-
-template <typename Stream, typename Pred, typename Proj = detail::identity>
-bool all_of(Stream const & stream, Pred pred, Proj proj = {}) {
-  return all_of(stream.begin(), stream.end(), pred, proj);
-}
-}
-
-#undef FWD

+ 89 - 0
include/stream/count.h

@@ -0,0 +1,89 @@
+//
+//  count.h
+//  stream
+//
+//  Created by Sam Jaffe on 3/30/23.
+//
+
+#pragma once
+
+#include <optional>
+
+#include <stream/detail/identity.h>
+#include <stream/detail/traits.h>
+
+#define FWD(x) std::forward<decltype(x)>(x)
+
+namespace stream::ranges {
+template <typename It, typename S, typename Pred,
+          typename Proj = detail::identity>
+bool any_of(It it, S end, Pred pred, Proj proj = {}) {
+  for (; it != end; ++it) {
+    if (std::invoke(pred, std::invoke(proj, *it))) { return true; }
+  }
+  return false;
+}
+
+template <typename Stream, typename Pred, typename Proj = detail::identity>
+bool any_of(Stream const & stream, Pred pred, Proj proj = {}) {
+  return any_of(stream.begin(), stream.end(), std::move(pred), std::move(proj));
+}
+
+template <typename It, typename S, typename Pred,
+          typename Proj = detail::identity>
+bool none_of(It it, S end, Pred pred, Proj proj = {}) {
+  return !any_of(it, end, std::move(pred), std::move(proj));
+}
+
+template <typename Stream, typename Pred, typename Proj = detail::identity>
+bool none_of(Stream const & stream, Pred pred, Proj proj = {}) {
+  return none_of(stream.begin(), stream.end(), std::move(pred),
+                 std::move(proj));
+}
+
+template <typename It, typename S, typename Pred,
+          typename Proj = detail::identity>
+bool all_of(It it, S end, Pred pred, Proj proj = {}) {
+  for (; it != end; ++it) {
+    if (!std::invoke(pred, std::invoke(proj, *it))) { return false; }
+  }
+  return true;
+}
+
+template <typename Stream, typename Pred, typename Proj = detail::identity>
+bool all_of(Stream const & stream, Pred pred, Proj proj = {}) {
+  return all_of(stream.begin(), stream.end(), std::move(pred), std::move(proj));
+}
+
+template <typename It, typename S, typename T, typename Proj = detail::identity>
+bool count(It it, S end, T const & value, Proj proj = {}) {
+  size_t result = 0;
+  for (; it != end; ++it) {
+    if (value == std::invoke(proj, *it)) { ++result; }
+  }
+  return result;
+}
+
+template <typename Stream, typename T, typename Proj = detail::identity>
+bool count(Stream const & stream, T const & value, Proj proj = {}) {
+  return count(stream.begin(), stream.end(), value, std::move(proj));
+}
+
+template <typename It, typename S, typename Pred,
+          typename Proj = detail::identity>
+auto count_if(It it, S end, Pred pred, Proj proj = {}) {
+  size_t result = 0;
+  for (; it != end; ++it) {
+    if (std::invoke(pred, std::invoke(proj, *it))) { ++result; }
+  }
+  return result;
+}
+
+template <typename Stream, typename Pred, typename Proj = detail::identity>
+auto count_if(Stream const & stream, Pred pred, Proj proj = {}) {
+  return count_if(stream.begin(), stream.end(), std::move(pred),
+                  std::move(proj));
+}
+}
+
+#undef FWD

+ 28 - 0
include/stream/detail/named_pair.h

@@ -0,0 +1,28 @@
+//
+//  named_pair.h
+//  stream
+//
+//  Created by Sam Jaffe on 3/30/23.
+//
+
+#pragma once
+
+#ifndef CONCAT
+#define CONCAT2(A, B) A##B
+#define CONCAT(A, B) CONCAT2(A, B)
+#endif
+
+#define NAMED_PAIR_CLASS_NAME(first, second)                                   \
+  CONCAT(CONCAT(CONCAT(first, _), second), _result)
+
+#define NAMED_PAIR(l, r)                                                       \
+  template <typename L, typename R> struct NAMED_PAIR_CLASS_NAME(l, r) {       \
+    NAMED_PAIR_CLASS_NAME(l, r)(L l, R r) : l(l), r(r) {}                      \
+    L l;                                                                       \
+    R r;                                                                       \
+  }
+
+namespace stream::detail {
+NAMED_PAIR(in, fun);
+NAMED_PAIR(min, max);
+}

+ 41 - 0
include/stream/fold.h

@@ -0,0 +1,41 @@
+//
+//  accumulate.h
+//  stream
+//
+//  Created by Sam Jaffe on 3/29/23.
+//
+
+#pragma once
+
+#include <optional>
+
+#include <stream/detail/identity.h>
+#include <stream/detail/traits.h>
+
+#define FWD(x) std::forward<decltype(x)>(x)
+
+namespace stream::ranges {
+template <typename It, typename S, typename T, typename F>
+auto fold_left(It it, S end, T init, F reduce) {
+  for (; it != end; ++it) {
+    init = std::invoke(reduce, init, *it);
+  }
+  return init;
+}
+
+template <typename Stream, typename T, typename F>
+auto fold_left(Stream && stream, T init, F reduce) {
+  for (auto && elem : FWD(stream)) {
+    init = std::invoke(reduce, init, FWD(elem));
+  }
+  return init;
+}
+
+template <typename Stream, typename F>
+auto fold_left_with_first(Stream && stream, F reduce) {
+  return fold_left(++stream.begin(), stream.end(), *stream.begin(),
+                   std::move(reduce));
+}
+}
+
+#undef FWD

+ 13 - 17
include/stream/for_each.h

@@ -7,29 +7,25 @@
 
 #pragma once
 
+#include <stream/detail/identity.h>
+#include <stream/detail/named_pair.h>
+
 #define FWD(x) std::forward<decltype(x)>(x)
 
 namespace stream::ranges {
-template <typename S, typename F> void for_each(S && stream, F func) {
-  for (auto && elem : FWD(stream)) {
-    func(FWD(elem));
+template <typename It, typename S, typename F, typename Proj = detail::identity>
+auto for_each(It it, S end, F func, Proj proj = {}) {
+  for (; it != end; ++it) {
+    std::invoke(func, std::invoke(proj, FWD(*it)));
   }
+  return detail::in_fun_result(it, std::move(func));
 }
-}
-
-namespace stream::ranges::views {
-template <typename F> class for_each {
-private:
-  F operation_;
 
-public:
-  for_each(F operation) : operation_(operation) {}
-
-  template <typename Stream>
-  friend void operator|(Stream && stream, for_each const & each) {
-    ranges::for_each(FWD(stream), each.operation_);
-  }
-};
+template <typename Stream, typename F, typename Proj = detail::identity>
+auto for_each(Stream && stream, F func, Proj proj = {}) {
+  return for_each(stream.begin(), stream.end(), std::move(func),
+                  std::move(proj));
+}
 }
 
 #undef FWD

+ 81 - 0
include/stream/minmax.h

@@ -0,0 +1,81 @@
+//
+//  minmax.h
+//  stream
+//
+//  Created by Sam Jaffe on 3/30/23.
+//
+
+#pragma once
+
+#include <optional>
+
+#include <stream/detail/identity.h>
+#include <stream/detail/named_pair.h>
+#include <stream/detail/traits.h>
+
+#define FWD(x) std::forward<decltype(x)>(x)
+
+namespace stream::detail {
+template <typename T, typename Comp, typename Proj>
+bool compare(T const & l, T const & r, Comp comp, Proj proj) {
+  return std::invoke(comp, std::invoke(proj, l), std::invoke(proj, r));
+}
+}
+
+namespace stream::ranges {
+
+template <typename It, typename S, typename Comp = std::less<>,
+          typename Proj = detail::identity>
+auto minmax(It it, S end, Comp comp = {}, Proj proj = {}) {
+  detail::min_max_result v(*it, *it);
+  for (++it; it != end; ++it) {
+    if (detail::compare(v.max, *it, std::ref(comp), std::ref(proj))) {
+      v.max = *it;
+    }
+    if (detail::compare(*it, v.min, std::ref(comp), std::ref(proj))) {
+      v.min = *it;
+    }
+  }
+  return v;
+}
+
+template <typename Stream, typename Comp = std::less<>,
+          typename Proj = detail::identity>
+auto minmax(Stream const & stream, Comp comp = {}, Proj proj = {}) {
+  return minmax(stream.begin(), stream.end(), std::move(comp), std::move(proj));
+}
+
+template <typename It, typename S, typename Comp = std::less<>,
+          typename Proj = detail::identity>
+auto min(It it, S end, Comp comp = {}, Proj proj = {}) {
+  auto v = *it;
+  for (++it; it != end; ++it) {
+    if (detail::compare(*it, v, std::ref(comp), std::ref(proj))) { v = *it; }
+  }
+  return v;
+}
+
+template <typename Stream, typename Comp = std::less<>,
+          typename Proj = detail::identity>
+auto min(Stream const & stream, Comp comp = {}, Proj proj = {}) {
+  return min(stream.begin(), stream.end(), std::move(comp), std::move(proj));
+}
+
+template <typename It, typename S, typename Comp = std::less<>,
+          typename Proj = detail::identity>
+auto max(It it, S end, Comp comp = {}, Proj proj = {}) {
+  auto v = *it;
+  for (++it; it != end; ++it) {
+    if (detail::compare(v, *it, std::ref(comp), std::ref(proj))) { v = *it; }
+  }
+  return v;
+}
+
+template <typename Stream, typename Comp = std::less<>,
+          typename Proj = detail::identity>
+auto max(Stream const & stream, Comp comp = {}, Proj proj = {}) {
+  return max(stream.begin(), stream.end(), std::move(comp), std::move(proj));
+}
+}
+
+#undef FWD

+ 3 - 1
include/stream/streams.hpp

@@ -18,7 +18,9 @@
 
 #include <stream/view.h>
 
-#include <stream/accumulate.h>
+#include <stream/count.h>
+#include <stream/fold.h>
 #include <stream/for_each.h>
+#include <stream/minmax.h>
 #include <stream/size.h>
 #include <stream/to.h>

+ 8 - 2
stream.xcodeproj/project.pbxproj

@@ -82,13 +82,16 @@
 		CD52827C29D4FB11001A84DE /* to.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = to.h; sourceTree = "<group>"; };
 		CD52827D29D50081001A84DE /* for_each.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = for_each.h; sourceTree = "<group>"; };
 		CD52827E29D50BFA001A84DE /* reference_view.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = reference_view.h; sourceTree = "<group>"; };
-		CD52827F29D50E24001A84DE /* accumulate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = accumulate.h; sourceTree = "<group>"; };
+		CD52827F29D50E24001A84DE /* fold.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = fold.h; sourceTree = "<group>"; };
 		CD52828029D51166001A84DE /* identity.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = identity.h; sourceTree = "<group>"; };
 		CD52828129D5133C001A84DE /* tuple_view.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = tuple_view.h; sourceTree = "<group>"; };
+		CD5A8D3529D63C05008C2A4F /* count.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = count.h; sourceTree = "<group>"; };
+		CD5A8D3B29D63DF5008C2A4F /* named_pair.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = named_pair.h; sourceTree = "<group>"; };
 		CD64CCB926232D6900770A30 /* xcode_gtest_helper.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = xcode_gtest_helper.h; sourceTree = "<group>"; };
 		CD6EBE1C29D5C61700F387C1 /* common_view.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = common_view.h; sourceTree = "<group>"; };
 		CD6EBE2329D5CAD900F387C1 /* size.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = size.h; sourceTree = "<group>"; };
 		CD6EBE2429D5CE6500F387C1 /* sentinal.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = sentinal.h; sourceTree = "<group>"; };
+		CD6EBE2629D63B9E00F387C1 /* minmax.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = minmax.h; sourceTree = "<group>"; };
 		CD9337281E3CD78B00699FF5 /* stream_test.cxx */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = stream_test.cxx; sourceTree = "<group>"; };
 		CDAA170121A3A738007BBA11 /* stream */ = {isa = PBXFileReference; lastKnownFileType = folder; name = stream; path = include/stream; sourceTree = "<group>"; };
 		CDE8545E24DEBEBF006FE7C7 /* README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = "<group>"; };
@@ -157,7 +160,9 @@
 				CD6EBE2329D5CAD900F387C1 /* size.h */,
 				CD52827D29D50081001A84DE /* for_each.h */,
 				CD52827C29D4FB11001A84DE /* to.h */,
-				CD52827F29D50E24001A84DE /* accumulate.h */,
+				CD52827F29D50E24001A84DE /* fold.h */,
+				CD5A8D3529D63C05008C2A4F /* count.h */,
+				CD6EBE2629D63B9E00F387C1 /* minmax.h */,
 				CD52827B29D4F764001A84DE /* view.h */,
 				CD5281F029D3B173001A84DE /* forward.h */,
 				CD5281F729D3B173001A84DE /* streams.hpp */,
@@ -169,6 +174,7 @@
 			isa = PBXGroup;
 			children = (
 				CD52828029D51166001A84DE /* identity.h */,
+				CD5A8D3B29D63DF5008C2A4F /* named_pair.h */,
 				CD5281EC29D3B173001A84DE /* traits.h */,
 				CD6EBE2429D5CE6500F387C1 /* sentinal.h */,
 			);

+ 1 - 1
test/stream_test.cxx

@@ -169,7 +169,7 @@ TEST(StreamTest, CanDereferenceElements) {
 TEST(StreamTest, CanForEachConsume) {
   int hits = 0;
   std::vector<int> input{1, 2, 3, 4, 5};
-  input | views::for_each([&hits](int) { ++hits; });
+  ranges::for_each(views::all(input), [&hits](int) { ++hits; });
 
   EXPECT_THAT(hits, Eq(5));
 }