Transience of .front in input vs. forward ranges
H. S. Teoh
hsteoh at quickfur.ath.cx
Tue Oct 30 17:56:32 PDT 2012
On Wed, Oct 31, 2012 at 12:00:50AM +0100, deadalnix wrote:
[...]
> It appears to me that invalidating .front is done for performances
> reasons most of the time. As this is the unsafe face of the coin, I
> strongly think that this shouldn't be the default behavior.
>
> To me the best solution is to provide a function or a property on
> ranges that provide the unsafe version of the range.
>
> It is easy to provide a default implementation as UFCS that is a
> NOOP so no code is being broken.
>
> With that design, it is possible to provide an always safe interface
> while allowing algorithm that can handle non persistent .front to
> run at full speed.
Hmm. I like this idea! This is much better than my proposal. So let's
say we change byLine() to always return a copy of the buffer, so that
.front is never invalidated. Then for algorithms that don't care about
.front being invalidated, they can do something like this:
auto myAlgo(R)(R inputRange) {
auto r = inputRange.fastRange;
while (!r.empty) {
auto x = r.front;
// make use of x
r.popFront(); // this invalidates x
}
return result;
}
void main() {
myAlgo(stdin.byLine());
}
We can use UFCS so that fastRange is always defined:
// Default implementation
auto fastRange(R)(R range) {
return range;
}
For byLine(), then, we have:
struct ByLine(...) {
// safe implementation
@property auto fastRange() {
struct UnsafeByLine(...) {
...
}
return UnsafeByLine(this);
}
}
I like this. Very clean, and doesn't break backward compatibility, and
allows select algorithms to be optimized for speed without affecting
everything else.
[...]
> If I use your permuting example, it should be done as follow :
>
> struct InvalidatingAllPermutations(T) {
> T[] current, first;
> bool done;
>
> this(T[] initialBuf) {
> first = initialBuf;
> current = first.dup;
> }
>
> void popFront() {
> nextPermutation(current);
> if (equal(current, first))
> done = true;
> }
>
> @property bool empty() {
> return !done;
> }
>
> @property T[] front() {
> return current;
> }
>
> auto save() {
> AllPermutations!T copy;
> copy.front = this.front;
> copy.first = this.first;
> copy.done = this.done;
> return copy;
> }
> }
>
> struct AllPermutations(T) {
> InvalidatingAllPermutations!T innerRange;
> alias innerRange this;
>
> T[] current;
>
> this(T[] initialBuf) {
> innerRange = InvalidatingAllPermutations!T(initialBuf);
> current = innerRange.front.dup;
> }
>
> @property T[] front() {
> return current;
> }
>
> void popFront() {
> innerRange.popFront();
> current = innerRange.front.dup;
> }
> }
>
> I do think this is the way that conciliate safe as default but still
> allow to be fast when needed, without adding much on most code.
+1. I like this better than my proposal.
T
--
Doubtless it is a good thing to have an open mind, but a truly open mind
should be open at both ends, like the food-pipe, with the capacity for
excretion as well as absorption. -- Northrop Frye
More information about the Digitalmars-d
mailing list