On Tuesday, 15 January 2013 at 05:51:16 UTC, Andrei Alexandrescu
wrote:
(Warning: this assumes a fair bit of background with ranges and
the STL.)
We've had a long-standing weakness of ranges when compared with
STL iterators: in a find() call we get to access the balance of
the range from the found position (if any) through the end of
the initial range. In contrast, STL's find returns an iterator
that can be combined with the beginning of the initial range or
the end of the initial range, thus offering two ranges instead
of one.
D offers the findSplit family which partially deals with this,
but in an arguably imperfect way because the range from the
beginning of the initial range to the found element has a
different type than the initial range.
In spite of that, things have worked out well.
Nowadays I've been thinking (inspired by an exchange on a C=+
mailing list) that STL's rfind (http://goo.gl/Btg8s) is a real
challenge for D's ranges.
In D, r.rfind(e) should return a range starting with the last
occurrence of e in r and spanning through the rest of r.
If r is a forward range but not better, this is rather simple:
R rfind(R, E)(R range, E element)
{
for (;;)
{
auto ahead = range.save.find(element);
if (ahead.empty) return range;
range = ahead;
}
}
If the range is random-access, the algorithm is easy to
implement efficiently by scanning from the end and using
indexing.
The tricky case is with bidirectional range. I was unable to
define an algorithm using the current primitives that: (a)
scans the range from the end, and (b) returns a range from the
found position through the end.
Looks like rfind() would need one extra primitive for
bidirectional ranges. What would be a good one? I have a few
starters, but don't want to bias anyone. Please chime in!
Thanks,
Andrei
Interesting problem. One possibility would be to add a CLASS
primitive like this: Range.merge( Range start, Range end )
//Takes the start of the start and the end of end.
Pseudo:
original = range.save
resPos = range.retro.find
return Range.merge( resPos, original ) //DOES NOT WORK
The problem with this solution is that:
Retro returns a different range type and it would be more
complicated to
have the merge function work with this type.
An addition to this solution could be another new primitive:
reverse:
Pseudo:
original = range.save
reversed = range.reverse //Permanently invert start an end. I
think this is feasible for all bidirectional ranges. Still a
bidirectional range.
reversed.find( needle ) //In haystack :)
return Range.merge( reversed, original ) //Ok, start of
reversed is on needle and end of original hasn't moved. The order
of the range returned is the same as the one received.
This is just a suggestion and any primitive could be renamed.
However, I think reverse is a good place to start.
Phil