Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions include/rusty_iterators/concepts.hpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#pragma once

#include <expected>
#include <optional>
#include <tuple>

Expand Down Expand Up @@ -59,6 +60,11 @@ concept Summable = requires(T first, T second) {
{ first + second } -> std::same_as<T>;
};

template <class B, class T, class Functor>
concept TryFoldFunctor = requires(Functor f, B first, T&& second) {
{ f(first, second) } -> std::same_as<std::expected<B, B>>;
};

template <class T>
concept TupleLike = requires(T t) {
std::tuple_size<T>::value == 2;
Expand Down
78 changes: 42 additions & 36 deletions include/rusty_iterators/interface.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -138,6 +139,10 @@ class IterInterface

[[nodiscard]] auto take(size_t amount) -> Take<T, Derived>;

template <class B, class Functor>
requires TryFoldFunctor<B, T, Functor>
[[nodiscard]] auto tryFold(B&& init, Functor&& f) -> B;

template <class R = T>
requires TupleLike<R>
[[nodiscard]] auto unzip() -> std::tuple<std::vector<typename std::tuple_element<0, R>::type>,
Expand All @@ -163,55 +168,31 @@ auto rusty_iterators::interface::IterInterface<T, Derived>::advanceBy(size_t amo
for (size_t i = 0; i < amount; i++)
{
[[unlikely]] if (!self().next().has_value())
{
break;
}
}
return std::move(self());
}

template <class T, class Derived>
template <class Functor>
requires rusty_iterators::concepts::AnyFunctor<T, Functor>
auto rusty_iterators::interface::IterInterface<T, Derived>::any(Functor&& f) -> bool
auto rusty_iterators::interface::IterInterface<T, Derived>::any(Functor&& f) -> bool // NOLINT
{
// TODO: move this to use `tryFold` when implemented.
sizeHintChecked();

auto func = std::forward<Functor>(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<Functor>(f)](bool acc, T x) {
return f(x) ? std::expected<bool, bool>{true} : std::unexpected{false};
};
return self().tryFold(false, std::move(anyf));
}

template <class T, class Derived>
template <class Functor>
requires rusty_iterators::concepts::AllFunctor<T, Functor>
auto rusty_iterators::interface::IterInterface<T, Derived>::all(Functor&& f) -> bool
auto rusty_iterators::interface::IterInterface<T, Derived>::all(Functor&& f) -> bool // NOLINT
{
// TODO: move this to use `tryFold` when implemented.
sizeHintChecked();

auto func = std::forward<Functor>(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<Functor>(f)](bool acc, T x) {
return !f(x) ? std::expected<bool, bool>{false} : std::unexpected{true};
};
return self().tryFold(true, std::move(allf));
}

template <class T, class Derived>
Expand Down Expand Up @@ -383,10 +364,9 @@ auto rusty_iterators::interface::IterInterface<T, Derived>::nth(size_t element)
template <class T, class Derived>
template <class Functor>
requires rusty_iterators::concepts::PositionFunctor<T, Functor>
auto rusty_iterators::interface::IterInterface<T, Derived>::position(Functor&& f)
auto rusty_iterators::interface::IterInterface<T, Derived>::position(Functor&& f) // NOLINT
-> std::optional<size_t>
{
// TODO: Move this to use `tryFold` when implemented.
sizeHintChecked();

auto func = std::forward<Functor>(f);
Expand All @@ -399,6 +379,7 @@ auto rusty_iterators::interface::IterInterface<T, Derived>::position(Functor&& f
{
return std::make_optional(position);
}
position += 1;
nextItem = self().next();
}
return std::nullopt;
Expand Down Expand Up @@ -432,6 +413,31 @@ auto rusty_iterators::interface::IterInterface<T, Derived>::take(size_t amount)
return Take<T, Derived>{std::forward<Derived>(self()), amount};
}

template <class T, class Derived>
template <class B, class Functor>
requires rusty_iterators::concepts::TryFoldFunctor<B, T, Functor>
auto rusty_iterators::interface::IterInterface<T, Derived>::tryFold(B&& init, Functor&& f) -> B
{
sizeHintChecked();

auto func = std::forward<Functor>(f);
auto accum = std::forward<B>(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.value();
}
accum = potentialAccum.error();
nextItem = self().next();
}
return std::move(accum);
}

template <class T, class Derived>
template <class R>
requires rusty_iterators::concepts::TupleLike<R>
Expand Down
11 changes: 11 additions & 0 deletions tests/iterator.test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<int, int>{acc} : std::unexpected{x + acc};
};
auto result = LazyIterator{vec}.tryFold(0, std::move(f));

ASSERT_EQ(result, 6);
}