0

Let's say that given a range like this

std::vector<int> v{1, 4, 7, 2};

I want to generate another range where all even numbers are repeated a number of times equal to their value, whereas all odd numbers are left unchanged.

A possible solution is the following:

#include <iostream>
#include <range/v3/view/join.hpp>
#include <range/v3/view/transform.hpp>
#include <range/v3/view/repeat_n.hpp>
#include <vector>

using namespace ranges::views;

auto f = [](auto x){
    return (x % 2) ? repeat_n(x,1) : repeat_n(x,x);
};

int main() {
    std::vector<int> v{1, 4, 7, 2};
    auto w = v | transform(f) | join;
    for (auto i : w)
        std::cout << i << std::endl;
}

However, using repeat_n(x,1) just in order to wrap x in a singleton range looks a bit clunky to me. Is there some ad-hoc function in Range-v3 to do this?

Enlico
  • 23,259
  • 6
  • 48
  • 102
  • 1
    [`ranges::views::single`](https://ericniebler.github.io/range-v3/structranges_1_1views_1_1single__fn.html) would be that, but it has a different type from `repeat_n` so you can't just use it in `?:`. Maybe `return repeat_n(x, (x % 2) ? 1 : x)` might be cleaner? – Artyer Feb 19 '21 at 17:41
  • @Artyer, I understand what you mean, and the problem is just that, I want the two things to be of the same type. Probably I have simplified the question too much. I will delete it for now and probably write a new one where explain more in details my needs. – Enlico Feb 19 '21 at 17:45
  • @Artyer, I've asked a more detailed question [here](https://stackoverflow.com/questions/66284017/how-to-wrap-some-computed-values-in-a-range-in-order-to-allow-using-join-on-the), if you like to give a look at it. – Enlico Feb 19 '21 at 19:41

1 Answers1

1

Monadic bind is spelt ranges::views::for_each in range-v3 so you can do

auto w = v
    | ranges::views::for_each(f)
    ;

for_each is just a wrapper around what you've already done transform(f) | join.

https://godbolt.org/z/jbbodYzrz


Woops may be I didn't read the question properly.

There's ranges::yield_if but this wouldn't help in you case.

If instead you where trying to return an empty range rather than a singleton, you could do

auto f = [](auto x){
        return ranges::yield_if( x % 2 == 1, ranges::views::repeat_n(x,x));
};

https://godbolt.org/z/bWKrW5b8b

Tom Huntington
  • 2,260
  • 10
  • 20
  • The statement that `for_each` is monadic binding is incorrect. For each is a powerful abstraction that doesn't even require that you pull anything out of it, as it's perfectly fine to pass to it a mutating lambda `-> void`. `for_each` can be used for doing monadic binding as much as to do functorial transform, applicative application and whatever you can think about (always limited to lists). – Enlico Sep 26 '22 at 20:25
  • 1
    Ranges have two applicatives, one uses `views::repeat` for pure `views::zip_with` for App. The other uses `views::single` for pure and it's App has to be made with `cartesian_product` and `transform`. I don't think `views::for_each` really helps with either. – Tom Huntington Sep 27 '22 at 21:26
  • Well actually `views::zip_with` would be pure+App – Tom Huntington Sep 27 '22 at 21:43
  • `ranges::views::for_each` is not `ranges::for_each`, you cant pass a function `->void` to `ranges::views::for_each` and it breaks the contract to pass a lambda that doesn't satisfy the regular invokable concept – Tom Huntington Sep 27 '22 at 21:46
  • 1
    @Enlico `ranges::views::for_each`'s lambda must return a range https://godbolt.org/z/rGhEGKPqe. It's unfortunately named the same as `ranges::for_each` as they are completely different functions. It should have been named `flat_map` – Tom Huntington Sep 27 '22 at 23:05
  • Oh, I thought `ranges::views::for_each` was just `ranges::for_each`, even though your call syntax should have suggested to me that was not the case. Thanks for clarifying. – Enlico Sep 27 '22 at 23:08