From 836a4b1d3ba05a761ab1859c820f2b76fc59a880 Mon Sep 17 00:00:00 2001 From: WiktorNowak Date: Wed, 8 Jan 2025 08:24:56 +0100 Subject: [PATCH 1/2] feat: add tryFold to iterator interface --- include/rusty_iterators/concepts.hpp | 6 ++ include/rusty_iterators/interface.hpp | 81 +++++++++++++++------------ 2 files changed, 51 insertions(+), 36 deletions(-) diff --git a/include/rusty_iterators/concepts.hpp b/include/rusty_iterators/concepts.hpp index 5f8a7a8..415241d 100644 --- a/include/rusty_iterators/concepts.hpp +++ b/include/rusty_iterators/concepts.hpp @@ -1,5 +1,6 @@ #pragma once +#include #include #include @@ -59,6 +60,11 @@ concept Summable = requires(T first, T second) { { first + second } -> std::same_as; }; +template +concept TryFoldFunctor = requires(Functor f, B first, T&& second) { + { f(first, second) } -> std::same_as>; +}; + template concept TupleLike = requires(T t) { std::tuple_size::value == 2; diff --git a/include/rusty_iterators/interface.hpp b/include/rusty_iterators/interface.hpp index aabba69..0f1c148 100644 --- a/include/rusty_iterators/interface.hpp +++ b/include/rusty_iterators/interface.hpp @@ -29,6 +29,7 @@ using concepts::Indexable; using concepts::InspectFunctor; using concepts::PositionFunctor; using concepts::Summable; +using concepts::TryFoldFunctor; using concepts::TupleLike; using iterator::CacheCycle; @@ -138,6 +139,10 @@ class IterInterface [[nodiscard]] auto take(size_t amount) -> Take; + template + requires TryFoldFunctor + [[nodiscard]] auto tryFold(B&& init, Functor&& f) -> B; + template requires TupleLike [[nodiscard]] auto unzip() -> std::tuple::type>, @@ -163,9 +168,7 @@ auto rusty_iterators::interface::IterInterface::advanceBy(size_t amo for (size_t i = 0; i < amount; i++) { [[unlikely]] if (!self().next().has_value()) - { break; - } } return std::move(self()); } @@ -173,45 +176,27 @@ auto rusty_iterators::interface::IterInterface::advanceBy(size_t amo template template requires rusty_iterators::concepts::AnyFunctor -auto rusty_iterators::interface::IterInterface::any(Functor&& f) -> bool +auto rusty_iterators::interface::IterInterface::any(Functor&& f) -> bool // NOLINT { - // TODO: move this to use `tryFold` when implemented. - sizeHintChecked(); - - auto func = std::forward(f); - auto nextItem = self().next(); - - [[likely]] while (nextItem.has_value()) - { - if (func(nextItem.value())) - { - return true; - } - nextItem = self().next(); - } - return false; + auto anyf = [f = std::forward(f)](bool acc, T x) -> std::expected { + if (f(x)) + return std::unexpected{true}; + return acc; + }; + return self().tryFold(false, std::move(anyf)); } template template requires rusty_iterators::concepts::AllFunctor -auto rusty_iterators::interface::IterInterface::all(Functor&& f) -> bool +auto rusty_iterators::interface::IterInterface::all(Functor&& f) -> bool // NOLINT { - // TODO: move this to use `tryFold` when implemented. - sizeHintChecked(); - - auto func = std::forward(f); - auto nextItem = self().next(); - - [[likely]] while (nextItem.has_value()) - { - if (!func(nextItem.value())) - { - return false; - } - nextItem = self().next(); - } - return true; + auto allf = [f = std::forward(f)](bool acc, T x) -> std::expected { + if (!f(x)) + return std::unexpected{false}; + return acc; + }; + return self().tryFold(true, std::move(allf)); } template @@ -383,10 +368,9 @@ auto rusty_iterators::interface::IterInterface::nth(size_t element) template template requires rusty_iterators::concepts::PositionFunctor -auto rusty_iterators::interface::IterInterface::position(Functor&& f) +auto rusty_iterators::interface::IterInterface::position(Functor&& f) // NOLINT -> std::optional { - // TODO: Move this to use `tryFold` when implemented. sizeHintChecked(); auto func = std::forward(f); @@ -399,6 +383,7 @@ auto rusty_iterators::interface::IterInterface::position(Functor&& f { return std::make_optional(position); } + position += 1; nextItem = self().next(); } return std::nullopt; @@ -432,6 +417,30 @@ auto rusty_iterators::interface::IterInterface::take(size_t amount) return Take{std::forward(self()), amount}; } +template +template + requires rusty_iterators::concepts::TryFoldFunctor +auto rusty_iterators::interface::IterInterface::tryFold(B&& init, Functor&& f) -> B +{ + sizeHintChecked(); + + auto func = std::forward(f); + auto accum = std::forward(init); + auto nextItem = self().next(); + + [[likely]] while (nextItem.has_value()) + { + auto potentialAccum = func(std::move(accum), std::move(nextItem.value())); + if (!potentialAccum.has_value()) + { + return potentialAccum.error(); + } + accum = potentialAccum.value(); + nextItem = self().next(); + } + return std::move(accum); +} + template template requires rusty_iterators::concepts::TupleLike From cf152d07992c7081cfe7ed547cf66247a927e03b Mon Sep 17 00:00:00 2001 From: WiktorNowak Date: Thu, 9 Jan 2025 06:55:53 +0100 Subject: [PATCH 2/2] refactor: swap the std::expected --- include/rusty_iterators/interface.hpp | 19 ++++++++----------- tests/iterator.test.cpp | 11 +++++++++++ 2 files changed, 19 insertions(+), 11 deletions(-) diff --git a/include/rusty_iterators/interface.hpp b/include/rusty_iterators/interface.hpp index 0f1c148..55f5cb7 100644 --- a/include/rusty_iterators/interface.hpp +++ b/include/rusty_iterators/interface.hpp @@ -178,10 +178,8 @@ template requires rusty_iterators::concepts::AnyFunctor auto rusty_iterators::interface::IterInterface::any(Functor&& f) -> bool // NOLINT { - auto anyf = [f = std::forward(f)](bool acc, T x) -> std::expected { - if (f(x)) - return std::unexpected{true}; - return acc; + auto anyf = [f = std::forward(f)](bool acc, T x) { + return f(x) ? std::expected{true} : std::unexpected{false}; }; return self().tryFold(false, std::move(anyf)); } @@ -191,10 +189,8 @@ template requires rusty_iterators::concepts::AllFunctor auto rusty_iterators::interface::IterInterface::all(Functor&& f) -> bool // NOLINT { - auto allf = [f = std::forward(f)](bool acc, T x) -> std::expected { - if (!f(x)) - return std::unexpected{false}; - return acc; + auto allf = [f = std::forward(f)](bool acc, T x) { + return !f(x) ? std::expected{false} : std::unexpected{true}; }; return self().tryFold(true, std::move(allf)); } @@ -431,11 +427,12 @@ auto rusty_iterators::interface::IterInterface::tryFold(B&& init, Fu [[likely]] while (nextItem.has_value()) { auto potentialAccum = func(std::move(accum), std::move(nextItem.value())); - if (!potentialAccum.has_value()) + + if (potentialAccum.has_value()) { - return potentialAccum.error(); + return potentialAccum.value(); } - accum = potentialAccum.value(); + accum = potentialAccum.error(); nextItem = self().next(); } return std::move(accum); diff --git a/tests/iterator.test.cpp b/tests/iterator.test.cpp index 1062d8d..c22ff60 100644 --- a/tests/iterator.test.cpp +++ b/tests/iterator.test.cpp @@ -318,3 +318,14 @@ TEST(TestIterator, TestEqBy) ASSERT_TRUE(result); } + +TEST(TestIterator, TestTryFoldEarlyExit) +{ + auto vec = std::vector{1, 2, 3, 4, 5}; + auto f = [](auto acc, auto x) { + return acc > 5 ? std::expected{acc} : std::unexpected{x + acc}; + }; + auto result = LazyIterator{vec}.tryFold(0, std::move(f)); + + ASSERT_EQ(result, 6); +}