From cff58795720432d97f9301d7b970d29e145d7afc Mon Sep 17 00:00:00 2001 From: Patrick Wuttke Date: Mon, 21 Aug 2023 01:48:43 +0200 Subject: [PATCH] Added filter() and some more iterator utilities. --- source/mijin/util/iterators.hpp | 270 +++++++++++++++++++++++++++++++- 1 file changed, 263 insertions(+), 7 deletions(-) diff --git a/source/mijin/util/iterators.hpp b/source/mijin/util/iterators.hpp index 9525e13..e0be6cd 100644 --- a/source/mijin/util/iterators.hpp +++ b/source/mijin/util/iterators.hpp @@ -5,6 +5,7 @@ #include #include +#include #include #include #include @@ -30,6 +31,29 @@ struct RangeRef } }; +template +struct RangeRef> +{ + std::pair iterators; + + TIterator begin() const + { + return iterators.first; + } + + TIterator end() const + { + return iterators.second; + } +}; + +// in case of pass-by-reference +template +struct RangeRef&> : RangeRef> {}; + +static_assert(std::is_same_v>>().iterators.first), int*>, "RangeRef for iterator pair not working."); +static_assert(std::is_same_v&>>().iterators.first), std::string_view::iterator>, "RangeRef for iterator pair not working."); + template struct is_range_adapter_type : std::false_type {}; @@ -199,7 +223,7 @@ struct ReplacingIterator ReplacingIterator operator--(int) noexcept { - EnumeratingIterator copy(*this); + ReplacingIterator copy(*this); --(*this); return copy; } @@ -254,10 +278,14 @@ struct MappingIterator using orig_value_type = typename std::iterator_traits::value_type; using difference_type = std::ptrdiff_t; using value_type = std::invoke_result_t; + using reference = value_type&; using iterator_category = std::bidirectional_iterator_tag; // TODO? + static_assert(!std::is_same_v, "Invalid mapping result type."); + TIterator base; TFunctor functor; + mutable std::optional result; MappingIterator(TIterator base_, TFunctor functor_) noexcept : base(base_), functor(std::move(functor_)) {} MappingIterator(const MappingIterator&) noexcept = default; @@ -266,20 +294,24 @@ struct MappingIterator MappingIterator& operator=(const MappingIterator&) noexcept = default; MappingIterator& operator=(MappingIterator&&) noexcept = default; - value_type operator*() const noexcept + reference operator*() const noexcept { - return functor(*base); + if (!result.has_value()) { + result = functor(*base); + } + return *result; } MappingIterator& operator++() noexcept { ++base; + result = std::nullopt; return *this; } MappingIterator operator++(int) noexcept { - ReplacingIterator copy(*this); + MappingIterator copy(*this); ++(*this); return copy; } @@ -287,19 +319,20 @@ struct MappingIterator MappingIterator& operator--() noexcept { --base; + result = std::nullopt; return *this; } MappingIterator operator--(int) noexcept { - EnumeratingIterator copy(*this); + MappingIterator copy(*this); --(*this); return copy; } bool operator==(const MappingIterator& other) const noexcept { - return functor == other.functor && base == other.base; + return base == other.base; // TODO: compare functor? -> doesn't always work and may be useless } bool operator!=(const MappingIterator& other) const noexcept @@ -311,7 +344,8 @@ struct MappingIterator template struct MappingRange : RangeAdapter { - using value_type = typename std::iterator_traits()))>::value_type; + // using value_type = typename std::iterator_traits()))>::value_type; + using value_type = typename std::iterator_traits>().begin())>::value_type; RangeRef base; TFunctor functor; @@ -336,6 +370,184 @@ auto map(TIterable&& iterable, TFunctor&& functor) }; } +template +struct OptionalMappingIterator +{ + using orig_value_type = typename std::iterator_traits::value_type; + using difference_type = std::ptrdiff_t; + using optional_type = std::invoke_result_t; + using value_type = std::decay_t().value())>; + using iterator_category = std::forward_iterator_tag; // TODO? + using reference = value_type&; + + static_assert(!std::is_same_v, "Invalid mapping result type."); + + TIterator base; + TIterator end; + TFunctor functor; + mutable optional_type result; // must be mutable so dereferencing can stay const, not nice :/ + + OptionalMappingIterator(TIterator base_, TIterator end_, TFunctor functor_) noexcept : base(base_), end(end_), functor(std::move(functor_)) { + if (base != end) + { + result = functor(*base); + if (!result.has_value()) { + ++(*this); + } + } + } + OptionalMappingIterator(const OptionalMappingIterator&) noexcept = default; + OptionalMappingIterator(OptionalMappingIterator&&) noexcept = default; + + OptionalMappingIterator& operator=(const OptionalMappingIterator&) noexcept = default; + OptionalMappingIterator& operator=(OptionalMappingIterator&&) noexcept = default; + + reference operator*() const noexcept + { + return *result; + } + + OptionalMappingIterator& operator++() noexcept + { + do + { + ++base; + result = base != end ? functor(*base) : std::nullopt; + } while (base != end && !result.has_value()); + return *this; + } + + OptionalMappingIterator operator++(int) noexcept + { + OptionalMappingIterator copy(*this); + ++(*this); + return copy; + } + + bool operator==(const OptionalMappingIterator& other) const noexcept + { + return base == other.base && end == other.end; // TODO: compare functor? -> doesn't always work and may be useless + } + + bool operator!=(const OptionalMappingIterator& other) const noexcept + { + return !(*this == other); + } +}; + +template +struct OptionalMappingRange : RangeAdapter +{ + // using value_type = typename std::iterator_traits()))>::value_type; + using value_type = typename std::iterator_traits>().begin())>::value_type; + + RangeRef base; + TFunctor functor; + + auto begin() const noexcept + { + return OptionalMappingIterator(base.begin(), base.end(), functor); + } + + auto end() const noexcept + { + return OptionalMappingIterator(base.end(), base.end(), functor); + } +}; + +template +auto mapOptional(TIterable&& iterable, TFunctor&& functor) +{ + return OptionalMappingRange{ + .base = {std::forward(iterable)}, + .functor = std::forward(functor) + }; +} + +template +struct FilteringIterator +{ + using difference_type = typename std::iterator_traits::difference_type; + using value_type = typename std::iterator_traits::value_type; + using iterator_category = std::forward_iterator_tag; // TODO? + using reference = typename std::iterator_traits::reference; + + TIterator base; + TIterator end; + TPredicate predicate; + + FilteringIterator(TIterator base_, TIterator end_, TPredicate predicate_) noexcept : base(base_), end(end_), predicate(std::move(predicate_)) { + if (base != end && !predicate(*base)) { + ++(*this); + } + } + FilteringIterator(const FilteringIterator&) noexcept = default; + FilteringIterator(FilteringIterator&&) noexcept = default; + + FilteringIterator& operator=(const FilteringIterator&) noexcept = default; + FilteringIterator& operator=(FilteringIterator&&) noexcept = default; + + reference operator*() const noexcept + { + return *base; + } + + FilteringIterator& operator++() noexcept + { + do + { + ++base; + } while (base != end && !predicate(*base)); + return *this; + } + + FilteringIterator operator++(int) noexcept + { + FilteringIterator copy(*this); + ++(*this); + return copy; + } + + bool operator==(const FilteringIterator& other) const noexcept + { + return base == other.base && end == other.end; // TODO: compare predicate? + } + + bool operator!=(const FilteringIterator& other) const noexcept + { + return !(*this == other); + } +}; + +template +struct FilteringRange : RangeAdapter +{ + // using value_type = typename std::iterator_traits()))>::value_type; + using value_type = typename std::iterator_traits>().begin())>::value_type; + + RangeRef base; + TPredicate predicate; + + auto begin() const noexcept + { + return FilteringIterator(base.begin(), base.end(), predicate); + } + + auto end() const noexcept + { + return FilteringIterator(base.end(), base.end(), predicate); + } +}; + +template +auto filter(TIterable&& iterable, TPredicate&& predicate) +{ + return FilteringRange{ + .base = {std::forward(iterable)}, + .predicate = std::forward(predicate) + }; +} + template struct ChainingIterator { @@ -524,6 +736,50 @@ auto operator|(TIterable&& iterable, Map mapper) { return map(std::forward(iterable), std::move(mapper.functor)); } + +template +struct MapOptional +{ + T functor; + + explicit MapOptional(T functor_) : functor(std::move(functor_)) {} +}; +template +MapOptional(T) -> MapOptional; + +template +auto operator|(TIterable&& iterable, MapOptional mapper) +{ + return mapOptional(std::forward(iterable), std::move(mapper.functor)); +} + +template +struct Filter +{ + T predicate; + + explicit Filter(T predicate_) : predicate(std::move(predicate_)) {} +}; +template +Filter(T) -> Filter; + +template +auto operator|(TIterable&& iterable, Filter filterer) +{ + return filter(std::forward(iterable), std::move(filterer.predicate)); +} + +template +struct Xth {}; + +using First = Xth<0>; +using Second = Xth<1>; + +template +auto operator|(TIterable&& iterable, Xth) +{ + return map(std::forward(iterable), [](auto&& element) { return std::get(element); } ); +} } } // namespace mijin