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

Reply via email to