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::pairs 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 identity 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::curryed 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.