I'm confused about ranges (input and forward in practice, in algorithms)

via Digitalmars-d digitalmars-d at puremagic.com
Thu Aug 13 17:33:28 PDT 2015


Despite using them all the time, I'm suddenly confused about 
ranges...

My understanding is that (for library-worth code) algorithms that 
consume ranges are supposed to use .save() to be compatible with 
both ref type and value type ranges. What about the reverse? Are 
algorithms supposed to try to avoid effectively .save()ing ranges 
(when they are value types) by not copying range variables? 
Furthermore, is it incorrect for an input range that is also not 
a forward range (or at least does not declare itself formally as 
a forward range by having a save()) to allow itself to be bitwise 
copied? (i.e., to effectively provide a save() behaviour).

To make it a more concrete, consider the following:

     struct ValInputRange
     {
         int v;
         int front() { return v; }
         void popFront() { ++v; }
         bool empty = false;
     }

     class RefInputRange
     {
         int v;
         int front() { return v; }
         void popFront() { ++v; }
         bool empty = false;
     }

     void main()
     {
         auto vl = ValInputRange();
         auto rf = new RefInputRange();

         assert(vl.startsWith([0, 1]) == true);
         assert(vl.startsWith([0, 1]) == true);

         assert(rf.startsWith([0, 1]) == true);
         assert(rf.startsWith([0, 1]) == false);

         writeln(vl.take(3)); // [0, 1, 2]
         writeln(rf.take(3)); // [1, 2, 3]
     }

- is startsWith() supposed to be transparently usable with both 
val-type ranges and val-type ranges? Currently it provides 
different semantics for these, arguably, as in the example.

- startsWith accepts an InputRange. What does this mean in 
practice? I'm used (perhaps incorrectly) to thinking about a 
proper InputRange (one that isn't also a ForwardRange, etc.) as 
one that provides a single pass. But many algorithms, such as 
startsWith, have multi-pass semantics: despite not save()ing the 
range explicitly, since startsWith() receives the range by value, 
it is copied, and therefore a subsequent pass is available.

- If the current (dual) behaviour is desired, shouldn't 
startsWith at least document how it mutates the inputed range? I 
mean, to me it wasn't obvious that the last element wouldn't be 
popped after the comparison (it's slightly extra work, but if I'm 
OK with dropping the other elements then I probably don't want 
the last element to remain either), just as an example.

- If not, shouldn't it accept the range by ref?

- IMO, there *really* should be a small reference page on ranges 
that included all of these more subtle points. The most 
reference-style page on ranges is perhaps the documentation for 
std.ranges.interfaces and it doesn't discuss many of these kinds 
of points). BTW, I thought that after the discussion on the 
subject (due to Walter's parser work) it had been decided that it 
was necessary to call empty() before first calling front(), but 
that page says "Calling r.front is allowed only if calling 
r.empty has, *or would have*, returned false." (emphasis added). 
What's the current consensus?

- shouldn't InputRanges be more explicit about their implicit 
ForwardRange'ness or lack thereof? For instance, is it 100% 
obvious to everybody which of these versions will be correct? For 
all acceptable implementations of File and .byLine()?

     void main()
     {
         import std.file : write;
         import std.stdio : File;
         import std.algorithm;

         write("/tmp/test.txt", "line one\nline two\nline three");
         auto lines = File("/tmp/test.txt").byLine;

         // guess which...

         version(one)
         {
             assert(lines.startsWith(["line one", "line two"]) == 
true);
             assert(lines.startsWith(["line one", "line two"]) == 
true);
         }

         version(two)
         {
             assert(lines.startsWith(["line one", "line two"]) == 
true);
             assert(lines.startsWith(["line two", "line three"]) 
== true);
         }
     }


More information about the Digitalmars-d mailing list