5

Using the Range-v3 (release 0.10.0) library I was trying to construct a range from a std::vector, transform it into another range and finally sort that range. I expected the sort step to produce another range that I could consume later. But the best I could come up with was this:

std::vector<std::string> const input { "2", "3", "1" };
using namespace ranges;
std::vector<int> output = input
    | views::transform([](std::string s) { return std::stoi(s); })
    | to<std::vector>()
    | actions::sort

Notice the use of to<std::vector>() after the transform step and before the sort step. This seems to allocate a new std::vector when all I wanted was to sort the range that the transform step had produced.

Why is there no view::sort ? It would fit nicely in the above composition of ranges.

Angle.Bracket
  • 1,438
  • 13
  • 29

1 Answers1

9

The transformed range is only a view, the elements are generated one at a time as the view is iterated. It can't be sorted as there is nowhere to store the sorted elements. A hypothetical implementation would also be inefficient as it would have to transform each element every time it needed to do a comparison for the sort.

Your solution is correct to store the transformed elements in a vector then sort them.

Alan Birtles
  • 32,622
  • 4
  • 31
  • 60
  • When you look at `Enumerable.OrderBy(...)` in C# or `Stream.sorted(...)` in Java: these functions both do accept and produce "Ranges" so they contribute to composability of their respective libraries. I also can't see any significant downside regarding efficiency compared to the procedural way of doing the same things in these languages. So I thought the same should be possible in C++. Has the author of ranges-v3 sacrificed style for speed in this case ? – Angle.Bracket Feb 21 '20 at 12:09
  • 2
    Both of those presumably generate a list of elements internally, the c++ api is just making you explicitly ask it to do so, that way you aren't surprised when sorting your million element view uses lots of memory – Alan Birtles Feb 21 '20 at 12:13
  • E.g see https://stackoverflow.com/questions/3329985/how-to-avoid-orderby-memory-usage-problems – Alan Birtles Feb 21 '20 at 12:17
  • I can see the point of making costly operations explicit here. But isn't the price of interrupting the sequence of ranges here a bit too high ? My problem with this design is that I am left with a `std::vector`after sorting when I originally started out using ranges. – Angle.Bracket Feb 21 '20 at 12:23
  • 2
    A vector is a range? – Alan Birtles Feb 21 '20 at 12:24
  • Well no, I guess. I tried to apply `views::filter` to the output of `actions::sort` and that didn't compile. – Angle.Bracket Feb 21 '20 at 12:30
  • As far as i can see it should work, though it'd presumably be more efficient to filter before sorting – Alan Birtles Feb 21 '20 at 13:02
  • 2
    It interrupts the view pipeline because a temporary vector is not a view. If you want further eager processing, use actions. If you want laziness, store the vector into a variable so that it's not a temporary. – T.C. Feb 21 '20 at 13:18
  • So on one hand a vector (or any container) is not necessarily a range and on the other hand you have to resort to a container when you want to e.g. sort a range. This is rather strange for a library that aims at composability. – Angle.Bracket Feb 21 '20 at 14:51