Since you wrote
The below is quasi human language/c++ code, written to describe what I wanted to achieve.
I'm adding this answer to show a solution that (maybe accepting to change our midset a bit) is truly like human language, because it tells a story, rather then using low lever for
loops and if
conditionals for which you have to pay attention on whether you've mistakely written <=
instead of <
and other stuff.
Here it is:
auto dividers = iota(2, num) | transform(both(id, mod(num)))
| filter(compose(equal_to(0), get_second))
| transform(get_first);
What does it do?
In short:
- takes numbers from 2 to
num-1
,
std::pair
s them up with the modulo of num
by each of them,
- retains only those with 0 modulo,
- pulls the original number throwing away the modulo.
More in detail:
- Iota is the ninth letter of the Greek alphabet, and
ranges::views::iota
(for which you can refer to std::ranges::views::iota
) is such that iota(n)
gives you back n, n+1, n+2, n+3, ...
, whereas if you pass it a second argument, as in our usecase, it gives you back only numbers up to and not including that: iota(n, n+m)
gives n, n+1, n+2, ..., n+m-1
.
- For
ranges::views::transform
you can refer to std::ranges::views::transfrom
; we are passing to it both(id, mod(num))
as the transforming function, which, as the name should imply, applies both the id
entity funtion and mod(num)
to its argument and gives you back a pair of those two results (it is not a standard function, I've written it at the bottom of this answer); mod(num)
, guess what, is such that nod(num)(n) == num % n
(ditto, I've defined mod
below, as a hana::curry
ed wrapper to %
).
- For
ranges::views::filter
you can refer to std::ranges::views::filter
; we are passing to it the mathematical composition, via boost::hana::compose
of two functions: get_second
, which takes the second entry of the pair coming from the transform
, i.e. the modulo, and equal_to(0)
which is predicate testing if something is zero (equal_to
is a curried std::equal_to<>{}
function object).
- Finally, we are pulling out only the first element of the pairs, via
transform(get_first)
.
As an improvement to the code above, you could add | cache1
between the first of the two tranform
and the filter
after it. The reason why, is explained in this post here on Stack Overflow and in this post on FluentC++.
Below are the helper functions I've used with each header they need.
std::get
is a template function, so it can't be passed around as an object could. BOOST_HOF_LIFT
can make an object out of it:
#include <boost/hof/lift.hpp>
#include <utility>
template<int N>
auto constexpr get = BOOST_HOF_LIFT(std::get<N>);
so we can write more verbose getters for our usecase of working on pairs:
auto constexpr get_first = get<0>;
auto constexpr get_second = get<1>;
std::equal_to
is already a function object, but we want to be able to pass its two arguments one by one, so we curry it:
#include <boost/hana/functional/curry.hpp>
#include <functional>
auto constexpr equal_to = curry<2>(std::equal_to<>{});
We want to do the same for %
, but that's on operator, so we'd have to manually wrap it; luckily the STL offers an object already, namely std::modulus<>{}
:
// same two headers as previous one
auto constexpr mod = curry<2>(std::modulus<>{});
Finally, here's my hand-made both
(which could be greately improved, generalized):
#include <utility>
auto constexpr both = [](auto const& f1, auto const& f2){
return [&f1, &f2](auto const& x){
return std::make_pair(f1(x), f2(x));
};
};
Here's a working demo on Compiler Explorer.