Skip to content

Commit

Permalink
Merge pull request #85 from tcbrindle/pr/select_by
Browse files Browse the repository at this point in the history
Add mask adaptor
  • Loading branch information
tcbrindle authored Jul 13, 2023
2 parents 4d6aa03 + 583f208 commit 12180d2
Show file tree
Hide file tree
Showing 8 changed files with 437 additions and 1 deletion.
29 changes: 28 additions & 1 deletion docs/reference/adaptors.rst
Original file line number Diff line number Diff line change
Expand Up @@ -221,7 +221,34 @@ Adaptors

.. function::
template <sequence Seq, std::copy_constructable Func> \
auto map(Seq seq, Func func) -> sequence auto
auto map(Seq seq, Func func) -> sequence auto;

``mask``
^^^^^^^^

.. function::
template <sequence Seq, sequence Mask> \
requires boolean_testable<element_t<Mask>> \
auto mask(Seq seq, Mask where) -> sequence auto;

Given a sequence of values and a sequence of booleans, :func:`mask` yields those elements of :var:`seq` for which the corresponding element of :var:`where` evaluates to :expr:`true`. Iteration is complete when either of the two input sequences is exhausted.

The returned sequence models the lowest common category of the two input sequences, up to :concept:`bidirectional_sequence`. It is also a :concept:`bounded_sequence` and a :concept:`sized_sequence` when both inputs model these concepts.

:param seq: A sequence of values
:param where: A sequence whose element type is convertible to :expr:`bool`

:returns: An adapted sequence whose elements are the elements of :var:`seq`, filtered by the corresponding elements of :var:`where`

:example:

.. literalinclude:: ../../example/docs/mask.cpp
:language: cpp
:dedent:
:lines: 16-30

:see also:
* :func:`flux::filter`

``pairwise``
^^^^^^^^^^^^
Expand Down
1 change: 1 addition & 0 deletions example/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ add_example(example-docs-cursors docs/cursors.cpp)
add_example(example-docs-cycle docs/cycle.cpp)
add_example(example-docs-drop docs/drop.cpp)
add_example(example-docs-ends-with docs/ends_with.cpp)
add_example(example-docs-mask docs/mask.cpp)
add_example(example-docs-prescan docs/prescan.cpp)
add_example(example-docs-read-only docs/read_only.cpp)
add_example(example-docs-repeat docs/repeat.cpp)
Expand Down
31 changes: 31 additions & 0 deletions example/docs/mask.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@

// Copyright (c) 2023 Tristan Brindle (tcbrindle at gmail dot com)
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)

#include <flux.hpp>

#include <cassert>
#include <string>
#include <vector>

using namespace std::literals;

int main()
{
std::array values{"one"sv, "two"sv, "three"sv, "four"sv, "five"sv};

std::array selectors{true, false, true, false, true};

// flux::mask() selects those elements of values for which the corresponding
// element of selectors is true
auto masked = flux::mask(values, selectors);
assert(flux::equal(masked, std::array{"one"sv, "three"sv, "five"sv}));

// Note that the selectors sequence can have any element type which is
// explicitly convertible to bool
std::array int_selectors{0, 0, 0, 1, 1};

auto masked2 = flux::mask(values, int_selectors);
assert(flux::equal(masked2, std::array{"four"sv, "five"sv}));
}
1 change: 1 addition & 0 deletions include/flux.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
#include <flux/op/from.hpp>
#include <flux/op/inplace_reverse.hpp>
#include <flux/op/map.hpp>
#include <flux/op/mask.hpp>
#include <flux/op/minmax.hpp>
#include <flux/op/read_only.hpp>
#include <flux/op/ref.hpp>
Expand Down
5 changes: 5 additions & 0 deletions include/flux/core/inline_sequence_base.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -266,6 +266,11 @@ struct inline_sequence_base {
[[nodiscard]]
constexpr auto map(Func func) &&;

template <adaptable_sequence Mask>
requires detail::boolean_testable<element_t<Mask>>
[[nodiscard]]
constexpr auto mask(Mask&& mask_) &&;

[[nodiscard]]
constexpr auto pairwise() && requires multipass_sequence<Derived>;

Expand Down
168 changes: 168 additions & 0 deletions include/flux/op/mask.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@

// Copyright (c) 2023 Tristan Brindle (tcbrindle at gmail dot com)
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)

#ifndef FLUX_OP_MASK_HPP_INCLUDED
#define FLUX_OP_MASK_HPP_INCLUDED

#include <flux/core.hpp>

namespace flux {

namespace detail {

template <sequence Base, sequence Mask>
struct mask_adaptor : inline_sequence_base<mask_adaptor<Base, Mask>>
{
private:
FLUX_NO_UNIQUE_ADDRESS Base base_;
FLUX_NO_UNIQUE_ADDRESS Mask mask_;

public:
constexpr mask_adaptor(decays_to<Base> auto&& base, decays_to<Mask> auto&& mask)
: base_(FLUX_FWD(base)),
mask_(FLUX_FWD(mask))
{}

struct flux_sequence_traits {
private:
struct cursor_type {
cursor_t<Base> base_cur;
cursor_t<Mask> mask_cur;

friend auto operator==(cursor_type const&, cursor_type const&) -> bool
requires std::equality_comparable<cursor_t<Base>> &&
std::equality_comparable<cursor_t<Mask>>
= default;
};

template <typename Self>
static inline constexpr bool maybe_const_iterable =
std::is_const_v<Self>
? sequence<Base const> && sequence<Mask const>
: true;

public:
using value_type = value_t<Base>;

static inline constexpr bool is_infinite =
infinite_sequence<Base> && infinite_sequence<Mask>;

template <typename Self>
requires maybe_const_iterable<Self>
static constexpr auto first(Self& self) -> cursor_type
{
auto base_cur = flux::first(self.base_);
auto mask_cur = flux::first(self.mask_);

while (!flux::is_last(self.base_, base_cur) && !flux::is_last(self.mask_, mask_cur)) {
if (static_cast<bool>(flux::read_at(self.mask_, mask_cur))) {
break;
}
flux::inc(self.base_, base_cur);
flux::inc(self.mask_, mask_cur);
}
return cursor_type{std::move(base_cur), std::move(mask_cur)};
}

template <typename Self>
requires maybe_const_iterable<Self>
static constexpr auto is_last(Self& self, cursor_type const& cur) -> bool
{
return flux::is_last(self.base_, cur.base_cur) ||
flux::is_last(self.mask_, cur.mask_cur);
}

template <typename Self>
requires maybe_const_iterable<Self>
static constexpr auto inc(Self& self, cursor_type& cur) -> void
{
while (!flux::is_last(self.base_, flux::inc(self.base_, cur.base_cur)) &&
!flux::is_last(self.mask_, flux::inc(self.mask_, cur.mask_cur))) {
if (static_cast<bool>(flux::read_at(self.mask_, cur.mask_cur))) {
break;
}
}
}

template <typename Self>
requires maybe_const_iterable<Self>
static constexpr auto read_at(Self& self, cursor_type const& cur) -> decltype(auto)
{
return flux::read_at(self.base_, cur.base_cur);
}

template <typename Self>
requires maybe_const_iterable<Self>
static constexpr auto read_at_unchecked(Self& self, cursor_type const& cur)
-> decltype(auto)
{
return flux::read_at_unchecked(self.base_, cur.base_cur);
}

template <typename Self>
requires maybe_const_iterable<Self>
static constexpr auto move_at(auto& self, cursor_type const& cur) -> decltype(auto)
{
return flux::move_at(self.base_, cur.base_cur);
}

template <typename Self>
requires maybe_const_iterable<Self>
static constexpr auto move_at_unchecked(Self& self, cursor_type const& cur)
-> decltype(auto)
{
return flux::move_at_unchecked(self.base_, cur.base_cur);
}

template <typename Self>
requires maybe_const_iterable<Self> &&
bounded_sequence<Base> &&
bounded_sequence<Mask>
static constexpr auto last(Self& self) -> cursor_type
{
return cursor_type{.base_cur = flux::last(self.base_),
.mask_cur = flux::last(self.mask_)};
}

template <typename Self>
requires maybe_const_iterable<Self> &&
bidirectional_sequence<Base> &&
bidirectional_sequence<Mask>
static constexpr auto dec(Self& self, cursor_type& cur) -> void
{
do {
flux::dec(self.base_, cur.base_cur);
flux::dec(self.mask_, cur.mask_cur);
} while (!static_cast<bool>(flux::read_at(self.mask_, cur.mask_cur)));
}
};
};

struct mask_fn {
template <adaptable_sequence Base, adaptable_sequence Mask>
requires boolean_testable<element_t<Mask>>
[[nodiscard]]
constexpr auto operator()(Base&& base, Mask&& mask) const
{
return mask_adaptor<std::decay_t<Base>, std::decay_t<Mask>>(
FLUX_FWD(base), FLUX_FWD(mask));
}
};

} // namespace detail

inline constexpr auto mask = detail::mask_fn{};

template <typename D>
template <adaptable_sequence Mask>
requires detail::boolean_testable<element_t<Mask>>
constexpr auto inline_sequence_base<D>::mask(Mask&& mask_) &&
{
return flux::mask(std::move(derived()), FLUX_FWD(mask_));
}

} // namespace flux

#endif // FLUX_OP_MASK_HPP_INCLUDED
1 change: 1 addition & 0 deletions test/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ add_executable(test-libflux
test_front_back.cpp
test_generator.cpp
test_map.cpp
test_mask.cpp
test_minmax.cpp
test_output_to.cpp
test_range_iface.cpp
Expand Down
Loading

0 comments on commit 12180d2

Please sign in to comment.