From a9672d41155c0d5a541491f4b229a720c3eaf9e6 Mon Sep 17 00:00:00 2001 From: WiktorNowak Date: Tue, 14 Jan 2025 08:02:51 +0100 Subject: [PATCH] feat: add skip to iterator interface --- include/rusty_iterators/interface.hpp | 10 +++++ include/rusty_iterators/skip.hpp | 50 +++++++++++++++++++++++ tests/skip.test.cpp | 57 +++++++++++++++++++++++++++ 3 files changed, 117 insertions(+) create mode 100644 include/rusty_iterators/skip.hpp create mode 100644 tests/skip.test.cpp diff --git a/include/rusty_iterators/interface.hpp b/include/rusty_iterators/interface.hpp index f75b1a0..634123e 100644 --- a/include/rusty_iterators/interface.hpp +++ b/include/rusty_iterators/interface.hpp @@ -9,6 +9,7 @@ #include "inspect.hpp" #include "map.hpp" #include "moving_window.hpp" +#include "skip.hpp" #include "take.hpp" #include "zip.hpp" @@ -45,6 +46,7 @@ using iterator::FilterMap; using iterator::Inspect; using iterator::Map; using iterator::MovingWindow; +using iterator::Skip; using iterator::Take; using iterator::Zip; @@ -149,6 +151,8 @@ class IterInterface requires ReduceFunctor [[nodiscard]] auto reduce(Functor&& f) -> std::optional; + [[nodiscard]] auto skip(size_t n) -> Skip; + template requires Summable [[nodiscard]] auto sum() -> R; @@ -443,6 +447,12 @@ auto rusty_iterators::interface::IterInterface::reduce(Functor&& f) return fold(std::move(first.value()), std::forward(f)); } +template +auto rusty_iterators::interface::IterInterface::skip(size_t n) -> Skip +{ + return Skip{std::forward(self()), n}; +} + template template requires rusty_iterators::concepts::Summable diff --git a/include/rusty_iterators/skip.hpp b/include/rusty_iterators/skip.hpp new file mode 100644 index 0000000..6805015 --- /dev/null +++ b/include/rusty_iterators/skip.hpp @@ -0,0 +1,50 @@ +#pragma once + +#include "interface.fwd.hpp" + +#include +#include + +namespace rusty_iterators::iterator +{ +using interface::IterInterface; + +template +class Skip : public IterInterface> +{ + public: + explicit Skip(Other&& it, size_t n) : it(std::forward(it)), n(n) {} + + auto next() -> std::optional; + [[nodiscard]] auto sizeHint() const -> std::optional; + + private: + Other it; + size_t n; +}; +} // namespace rusty_iterators::iterator + +template +auto rusty_iterators::iterator::Skip::next() -> std::optional +{ + [[unlikely]] if (n > 0) + { + it = it.advanceBy(n); + n = 0; + return std::move(it.next()); + } + return std::move(it.next()); +} + +template +auto rusty_iterators::iterator::Skip::sizeHint() const -> std::optional +{ + auto underlyingSize = it.sizeHint(); + + if (!underlyingSize.has_value()) + { + return std::nullopt; + } + int32_t estimatedSize = underlyingSize.value() - n; + return std::max(0, estimatedSize); +} diff --git a/tests/skip.test.cpp b/tests/skip.test.cpp new file mode 100644 index 0000000..de6109a --- /dev/null +++ b/tests/skip.test.cpp @@ -0,0 +1,57 @@ +#include +#include + +#include + +using ::rusty_iterators::iterator::LazyIterator; +using ::testing::ElementsAreArray; + +TEST(TestSkipIterator, NextSkipsElements) +{ + auto vec = std::vector{1, 2, 3, 4, 5}; + auto it = LazyIterator{vec}.skip(3); + + ASSERT_EQ(it.next(), 4); + ASSERT_EQ(it.next(), 5); + ASSERT_EQ(it.next(), std::nullopt); +} + +TEST(TestSkipIterator, NextOnTooSmallIterator) +{ + auto vec = std::vector{1, 2, 3}; + auto it = LazyIterator{vec}.skip(5); + + ASSERT_EQ(it.next(), std::nullopt); +} + +TEST(TestSkipIterator, CollectAllItemsExceptSkippedOnes) +{ + auto vec = std::vector{1, 2, 3, 4, 5}; + auto it = LazyIterator{vec}.skip(3); + + EXPECT_THAT(it.collect(), ElementsAreArray({4, 5})); +} + +TEST(TestSkipIterator, SizeHintGivesEstimatedSize) +{ + auto vec = std::vector{1, 2, 3, 4, 5}; + auto it = LazyIterator{vec}.skip(3); + + ASSERT_EQ(it.sizeHint(), 2); +} + +TEST(TestSkipIterator, SizeHintOnInfiniteIterator) +{ + auto vec = std::vector{1, 2, 3}; + auto it = LazyIterator{vec}.cycle().skip(3); + + ASSERT_EQ(it.sizeHint(), std::nullopt); +} + +TEST(TestSkipIterator, TestSizeHintOnTooSmallIterator) +{ + auto vec = std::vector{1, 2, 3}; + auto it = LazyIterator{vec}.skip(10); + + ASSERT_EQ(it.sizeHint(), 0); +}