Search code examples
c++std-rangesc++23

Scaling an input range by minimum in (upcoming) C++23 (using zip_transform and repeat)


In ADSP Podcast Episode 91 (2022-08-19, around 14:30 ... 16:30) Bryce Lelbach and Conor Hoekstra talk about an example application of the (hopefully) upcoming C++23 ranges views zip_transform and repeat: scaling a range of values by the (non-zero) minimum value of that range.

Since to my knowledge at the time of writing, no library of any compiler implements these yet (and no link was given), I wonder how to implement it. The closest I came up with, was

auto scale_by_min(auto&& range)
{
    auto m = std::ranges::min(range);
    return std::views::zip_transform(
        range,
        std::views::repeat(m),
        [](auto x, auto min)
        {
            return x / min;
        }
    );
} 

((non-working) example in godbolt). Did I get it right?

Edit / Clarification:

Replacing the return statement by

return range | std::views::transform([m](auto x){ return x / m; });

it works in C++20, also without zip_transform and repeat, as correctly stated in a comment. This question is not about conciseness, style, or meaningfulness of the example. I just try to get the syntax right for using the upcoming ranges-views.


Solution

  • Did I get it right?

    No. zip_transform accepts variadic template arguments as input ranges, the first argument of zip_transform must be the transform functor, so you should

    auto m = std::ranges::min(range);
    return std::views::zip_transform(
      std::divides{},
      range,
      std::views::repeat(m)
    );
    

    It's worth noting that views::repeat(m) gives you an infinite range, which makes zip_transform_view not model sized_range. You can pass the second parameter of repeat_view by detecting whether the range is sized_range, e.g.

    auto m = std::ranges::min(range);
    auto repeat = [&] {
      if constexpr (std::ranges::sized_range<decltype(range)>)
        return std::views::repeat(m, std::ranges::size(range));
      else
        return std::views::repeat(m);
    }();
    return std::views::zip_transform(std::divides{}, range, repeat);
    

    which makes zip_transform_view preserve the properties of sized_range.