Added filter() and some more iterator utilities.

This commit is contained in:
Patrick 2023-08-21 01:48:43 +02:00
parent 2faae0e5ee
commit cff5879572

View File

@ -5,6 +5,7 @@
#include <cstddef> #include <cstddef>
#include <functional> #include <functional>
#include <optional>
#include <span> #include <span>
#include <string_view> #include <string_view>
#include <tuple> #include <tuple>
@ -30,6 +31,29 @@ struct RangeRef
} }
}; };
template<typename TIterator>
struct RangeRef<std::pair<TIterator, TIterator>>
{
std::pair<TIterator, TIterator> iterators;
TIterator begin() const
{
return iterators.first;
}
TIterator end() const
{
return iterators.second;
}
};
// in case of pass-by-reference
template<typename TIterator>
struct RangeRef<std::pair<TIterator, TIterator>&> : RangeRef<std::pair<TIterator, TIterator>> {};
static_assert(std::is_same_v<decltype(std::declval<RangeRef<std::pair<int*, int*>>>().iterators.first), int*>, "RangeRef for iterator pair not working.");
static_assert(std::is_same_v<decltype(std::declval<RangeRef<std::pair<std::string_view::iterator, std::string_view::iterator>&>>().iterators.first), std::string_view::iterator>, "RangeRef for iterator pair not working.");
template<typename T> template<typename T>
struct is_range_adapter_type : std::false_type {}; struct is_range_adapter_type : std::false_type {};
@ -199,7 +223,7 @@ struct ReplacingIterator
ReplacingIterator operator--(int) noexcept ReplacingIterator operator--(int) noexcept
{ {
EnumeratingIterator copy(*this); ReplacingIterator copy(*this);
--(*this); --(*this);
return copy; return copy;
} }
@ -254,10 +278,14 @@ struct MappingIterator
using orig_value_type = typename std::iterator_traits<TIterator>::value_type; using orig_value_type = typename std::iterator_traits<TIterator>::value_type;
using difference_type = std::ptrdiff_t; using difference_type = std::ptrdiff_t;
using value_type = std::invoke_result_t<TFunctor, orig_value_type>; using value_type = std::invoke_result_t<TFunctor, orig_value_type>;
using reference = value_type&;
using iterator_category = std::bidirectional_iterator_tag; // TODO? using iterator_category = std::bidirectional_iterator_tag; // TODO?
static_assert(!std::is_same_v<value_type, void>, "Invalid mapping result type.");
TIterator base; TIterator base;
TFunctor functor; TFunctor functor;
mutable std::optional<value_type> result;
MappingIterator(TIterator base_, TFunctor functor_) noexcept : base(base_), functor(std::move(functor_)) {} MappingIterator(TIterator base_, TFunctor functor_) noexcept : base(base_), functor(std::move(functor_)) {}
MappingIterator(const MappingIterator&) noexcept = default; MappingIterator(const MappingIterator&) noexcept = default;
@ -266,20 +294,24 @@ struct MappingIterator
MappingIterator& operator=(const MappingIterator&) noexcept = default; MappingIterator& operator=(const MappingIterator&) noexcept = default;
MappingIterator& operator=(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 MappingIterator& operator++() noexcept
{ {
++base; ++base;
result = std::nullopt;
return *this; return *this;
} }
MappingIterator operator++(int) noexcept MappingIterator operator++(int) noexcept
{ {
ReplacingIterator copy(*this); MappingIterator copy(*this);
++(*this); ++(*this);
return copy; return copy;
} }
@ -287,19 +319,20 @@ struct MappingIterator
MappingIterator& operator--() noexcept MappingIterator& operator--() noexcept
{ {
--base; --base;
result = std::nullopt;
return *this; return *this;
} }
MappingIterator operator--(int) noexcept MappingIterator operator--(int) noexcept
{ {
EnumeratingIterator copy(*this); MappingIterator copy(*this);
--(*this); --(*this);
return copy; return copy;
} }
bool operator==(const MappingIterator& other) const noexcept 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 bool operator!=(const MappingIterator& other) const noexcept
@ -311,7 +344,8 @@ struct MappingIterator
template<typename TIterable, typename TFunctor> template<typename TIterable, typename TFunctor>
struct MappingRange : RangeAdapter struct MappingRange : RangeAdapter
{ {
using value_type = typename std::iterator_traits<decltype(std::begin(std::declval<TIterable>()))>::value_type; // using value_type = typename std::iterator_traits<decltype(std::begin(std::declval<TIterable>()))>::value_type;
using value_type = typename std::iterator_traits<decltype(std::declval<RangeRef<TIterable>>().begin())>::value_type;
RangeRef<TIterable> base; RangeRef<TIterable> base;
TFunctor functor; TFunctor functor;
@ -336,6 +370,184 @@ auto map(TIterable&& iterable, TFunctor&& functor)
}; };
} }
template<typename TIterator, typename TFunctor>
struct OptionalMappingIterator
{
using orig_value_type = typename std::iterator_traits<TIterator>::value_type;
using difference_type = std::ptrdiff_t;
using optional_type = std::invoke_result_t<TFunctor, orig_value_type>;
using value_type = std::decay_t<decltype(std::declval<optional_type>().value())>;
using iterator_category = std::forward_iterator_tag; // TODO?
using reference = value_type&;
static_assert(!std::is_same_v<value_type, void>, "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<typename TIterable, typename TFunctor>
struct OptionalMappingRange : RangeAdapter
{
// using value_type = typename std::iterator_traits<decltype(std::begin(std::declval<TIterable>()))>::value_type;
using value_type = typename std::iterator_traits<decltype(std::declval<RangeRef<TIterable>>().begin())>::value_type;
RangeRef<TIterable> 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<typename TIterable, typename TFunctor>
auto mapOptional(TIterable&& iterable, TFunctor&& functor)
{
return OptionalMappingRange<TIterable, TFunctor>{
.base = {std::forward<TIterable>(iterable)},
.functor = std::forward<TFunctor>(functor)
};
}
template<typename TIterator, typename TPredicate>
struct FilteringIterator
{
using difference_type = typename std::iterator_traits<TIterator>::difference_type;
using value_type = typename std::iterator_traits<TIterator>::value_type;
using iterator_category = std::forward_iterator_tag; // TODO?
using reference = typename std::iterator_traits<TIterator>::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<typename TIterable, typename TPredicate>
struct FilteringRange : RangeAdapter
{
// using value_type = typename std::iterator_traits<decltype(std::begin(std::declval<TIterable>()))>::value_type;
using value_type = typename std::iterator_traits<decltype(std::declval<RangeRef<TIterable>>().begin())>::value_type;
RangeRef<TIterable> 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<typename TIterable, typename TPredicate>
auto filter(TIterable&& iterable, TPredicate&& predicate)
{
return FilteringRange<TIterable, TPredicate>{
.base = {std::forward<TIterable>(iterable)},
.predicate = std::forward<TPredicate>(predicate)
};
}
template<typename TFirstIterator, typename TSecondIterator> template<typename TFirstIterator, typename TSecondIterator>
struct ChainingIterator struct ChainingIterator
{ {
@ -524,6 +736,50 @@ auto operator|(TIterable&& iterable, Map<TFunctor> mapper)
{ {
return map(std::forward<TIterable>(iterable), std::move(mapper.functor)); return map(std::forward<TIterable>(iterable), std::move(mapper.functor));
} }
template<typename T>
struct MapOptional
{
T functor;
explicit MapOptional(T functor_) : functor(std::move(functor_)) {}
};
template<typename T>
MapOptional(T) -> MapOptional<T>;
template<typename TIterable, typename TFunctor>
auto operator|(TIterable&& iterable, MapOptional<TFunctor> mapper)
{
return mapOptional(std::forward<TIterable>(iterable), std::move(mapper.functor));
}
template<typename T>
struct Filter
{
T predicate;
explicit Filter(T predicate_) : predicate(std::move(predicate_)) {}
};
template<typename T>
Filter(T) -> Filter<T>;
template<typename TIterable, typename TFilter>
auto operator|(TIterable&& iterable, Filter<TFilter> filterer)
{
return filter(std::forward<TIterable>(iterable), std::move(filterer.predicate));
}
template<std::size_t idx>
struct Xth {};
using First = Xth<0>;
using Second = Xth<1>;
template<typename TIterable, std::size_t idx>
auto operator|(TIterable&& iterable, Xth<idx>)
{
return map(std::forward<TIterable>(iterable), [](auto&& element) { return std::get<idx>(element); } );
}
} }
} // namespace mijin } // namespace mijin