| Thread overview | |||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
May 20, 2009 Re: "the last change" for ranges | ||||
|---|---|---|---|---|
| ||||
Andrei Alexandrescu Wrote: > In wake of a few discussion I've witnessed, I'm thinking of a last change for ranges. (In fact there's one more, but that's minor.) > > The problem is that input ranges and forward ranges have the same syntactic interface, but different semantic interfaces. Consider the problem of finding the first two identical adjacent items in a range: > > R adjacentFind(R)(R r) > { > if (r.empty) return r; > R last = r; > r.popFront; > for (; !r.empty && last.front != r.front; last.popFront, r.popFront) > { > } > return r; > } > > This will work properly on lists and vectors, but horrendously on files and sockets. This is because input ranges can't be saved for later use: incrementing r also increments popFront and essentially forces both to look at the same current value. > I think that if > R last = r; then after > r.popFront; the order of elements in "last" should not change, no matter what type of range you are dealing with. (That means that input operations would be buffered to the leftmost range that still extsts.) If I understood the logic of ranges, popFront() just changes the range, and not the elements it points to. | ||||
May 21, 2009 Re: "the last change" for ranges | ||||
|---|---|---|---|---|
| ||||
Posted in reply to MLT | MLT wrote:
> I think that if
>> R last = r;
> then after
>> r.popFront;
> the order of elements in "last" should not change, no matter what
> type of range you are dealing with. (That means that input operations
> would be buffered to the leftmost range that still extsts.)
>
> If I understood the logic of ranges, popFront() just changes the
> range, and not the elements it points to.
That's the case for all ranges except input ranges. Consider:
FileByCharacter
{
private FILE* _f;
private dchar _last;
bool empty() { return _last == 0xffff; }
void popFront() { _last = fgetc(_f); }
dchar front() { return _last; }
}
Consider what happens when you copy this range around.
Andrei
| |||
May 21, 2009 Re: "the last change" for ranges | ||||
|---|---|---|---|---|
| ||||
Posted in reply to Andrei Alexandrescu | Andrei Alexandrescu Wrote:
> MLT wrote:
> > I think that if
> >> R last = r;
> > then after
> >> r.popFront;
> > the order of elements in "last" should not change, no matter what type of range you are dealing with. (That means that input operations would be buffered to the leftmost range that still extsts.)
> >
> > If I understood the logic of ranges, popFront() just changes the range, and not the elements it points to.
>
> That's the case for all ranges except input ranges. Consider:
>
> FileByCharacter
> {
> private FILE* _f;
> private dchar _last;
> bool empty() { return _last == 0xffff; }
> void popFront() { _last = fgetc(_f); }
> dchar front() { return _last; }
> }
>
> Consider what happens when you copy this range around.
>
>
> Andrei
You didn't declare FileByCharacter as either a struct or a class? Did I plant a seed? :)
| |||
May 21, 2009 Re: "the last change" for ranges | ||||
|---|---|---|---|---|
| ||||
Posted in reply to Andrei Alexandrescu | Andrei Alexandrescu Wrote:
> MLT wrote:
> > I think that if
> >> R last = r;
> > then after
> >> r.popFront;
> > the order of elements in "last" should not change, no matter what type of range you are dealing with. (That means that input operations would be buffered to the leftmost range that still extsts.)
> >
> > If I understood the logic of ranges, popFront() just changes the range, and not the elements it points to.
>
> That's the case for all ranges except input ranges. Consider:
>
> FileByCharacter
> {
> private FILE* _f;
> private dchar _last;
> bool empty() { return _last == 0xffff; }
> void popFront() { _last = fgetc(_f); }
> dchar front() { return _last; }
> }
>
> Consider what happens when you copy this range around.
>
>
> Andrei
I thought more of something like:
FileByCharacter
{
private FILE* _f;
private dchar[] _buf;
bool empty() { return _buf[0] == 0xffff; }
void popFront() {
_buf = _buf[1..$] ;
if( _buf.length < 1 ) _buf ~= fgetc(_f);
}
dchar front() { return _buf[0]; }
}
The idea is that you continue to expand an array. Another copy of the range will continue to step over the same array. This doesn't really work, because the second copy doesn't really know how much the first copy already read. But that should be fixable....
the problem is in the line
if( _buf.length < 1 ) _buf ~= fgetc(_f);
which should only trigger if _buf reached the end of the read part, not the end of the current copy of _buf.
I'm also not sure if D's GC will handle dropping the part of the array that no one looks at.
One needs something like a lazy semi-infinite range, that only calls a certain function when it reaches an unexplored part.
| |||
May 21, 2009 Re: "the last change" for ranges | ||||
|---|---|---|---|---|
| ||||
Posted in reply to MLT | MLT wrote:
> One needs something like a lazy semi-infinite range, that only calls
> a certain function when it reaches an unexplored part.
That's a great abstraction, but we can't afford to impose that to everybody. There must be an abstraction for a one-pass go through an arbitrarily long stream.
Anyhow, I decided to change ranges as follows:
a) Input:
bool empty();
ref T popNext();
b) Output:
void putNext(T);
c) Forward:
bool empty();
ref T front();
void popFront();
The function ref T popNext() is a nonmember that accepts any forward range and uses front() and popFront(). Of course, a forward range is welcome to implement popFront as a member if it so wishes. The function putNext() overwrites front() and then calls popFront.
d) Bidirectional:
bool empty();
ref T front();
void popFront();
ref T back();
void popBack();
popNext, putNext apply as for forward ranges.
e) Random
bool empty();
ref T front();
void popFront();
ref T back();
void popBack();
ref T opIndex(uint n);
void opIndexAssign(T, uint n);
popNext, putNext apply as for forward ranges. We need to fix the opIndexAssign mess.
Andrei
| |||
May 21, 2009 Re: "the last change" for ranges | ||||
|---|---|---|---|---|
| ||||
Posted in reply to Andrei Alexandrescu | == Quote from Andrei Alexandrescu (SeeWebsiteForEmail@erdani.org)'s article > MLT wrote: > > One needs something like a lazy semi-infinite range, that only calls a certain function when it reaches an unexplored part. > That's a great abstraction, but we can't afford to impose that to > everybody. There must be an abstraction for a one-pass go through an > arbitrarily long stream. > Anyhow, I decided to change ranges as follows: > a) Input: > bool empty(); > ref T popNext(); > b) Output: > void putNext(T); > c) Forward: > bool empty(); > ref T front(); > void popFront(); > The function ref T popNext() is a nonmember that accepts any forward > range and uses front() and popFront(). Of course, a forward range is > welcome to implement popFront as a member if it so wishes. The function > putNext() overwrites front() and then calls popFront. > d) Bidirectional: > bool empty(); > ref T front(); > void popFront(); > ref T back(); > void popBack(); > popNext, putNext apply as for forward ranges. > e) Random > bool empty(); > ref T front(); > void popFront(); > ref T back(); > void popBack(); > ref T opIndex(uint n); > void opIndexAssign(T, uint n); > popNext, putNext apply as for forward ranges. We need to fix the > opIndexAssign mess. > Andrei Please, please, please PLEASE, PRETTY PLEASE FOR THE LOVE OF GOD ALMIGHTY tell me you're not serious!!! Isn't changing the interface such that forward ranges are no longer effectively a subtype of input ranges a bit drastic? Or do you have some magic up your sleeve that, given any forward range, will automatically call popFront, and then front, when popNext is called, using extension function hacks or something? The whole beauty of ranges is that they provide one standard interface to program to if all you need is a lowest common denominator level of functionality. Frankly, if you destroy this, I think that just enforcing forward vs. input ranges purely by convention would be the lesser of two evils. | |||
May 21, 2009 Re: "the last change" for ranges | ||||
|---|---|---|---|---|
| ||||
Posted in reply to Andrei Alexandrescu | == Quote from Andrei Alexandrescu (SeeWebsiteForEmail@erdani.org)'s article > MLT wrote: > > One needs something like a lazy semi-infinite range, that only calls a certain function when it reaches an unexplored part. > That's a great abstraction, but we can't afford to impose that to > everybody. There must be an abstraction for a one-pass go through an > arbitrarily long stream. > Anyhow, I decided to change ranges as follows: > a) Input: > bool empty(); > ref T popNext(); > b) Output: > void putNext(T); > c) Forward: > bool empty(); > ref T front(); > void popFront(); > The function ref T popNext() is a nonmember that accepts any forward > range and uses front() and popFront(). Of course, a forward range is > welcome to implement popFront as a member if it so wishes. The function > putNext() overwrites front() and then calls popFront. > d) Bidirectional: > bool empty(); > ref T front(); > void popFront(); > ref T back(); > void popBack(); > popNext, putNext apply as for forward ranges. > e) Random > bool empty(); > ref T front(); > void popFront(); > ref T back(); > void popBack(); > ref T opIndex(uint n); > void opIndexAssign(T, uint n); > popNext, putNext apply as for forward ranges. We need to fix the > opIndexAssign mess. > Andrei (Bangs head against desk.) Sorry. Didn't see the part about the non-member popNext(), though this would require Walter to make extension methods work for structs, which should probably happen anyway. | |||
May 21, 2009 Re: "the last change" for ranges | ||||
|---|---|---|---|---|
| ||||
Posted in reply to dsimcha | dsimcha wrote: > Please, please, please PLEASE, PRETTY PLEASE FOR THE LOVE OF GOD ALMIGHTY tell me > you're not serious!!! Isn't changing the interface such that forward ranges are > no longer effectively a subtype of input ranges a bit drastic? Or do you have > some magic up your sleeve that, given any forward range, will automatically call > popFront, and then front, when popNext is called, using extension function hacks > or something? Consider: struct R { bool empty(); ref int front(); void popFront(); } ref int popNext(ref R fwdRange) { auto result = & fwdRange.front(); fwdRange.popFront; return *result; } void main() { R r; int x = r.popNext; } This should work, I just noticed with surprise it doesn't. It's a bug, specifically bug 3015: http://d.puremagic.com/issues/show_bug.cgi?id=3015 > The whole beauty of ranges is that they provide one standard interface to program > to if all you need is a lowest common denominator level of functionality. > Frankly, if you destroy this, I think that just enforcing forward vs. input ranges > purely by convention would be the lesser of two evils. You and I see eye to eye. Andrei | |||
May 21, 2009 Re: "the last change" for ranges | ||||
|---|---|---|---|---|
| ||||
Posted in reply to dsimcha | dsimcha wrote:
> == Quote from Andrei Alexandrescu (SeeWebsiteForEmail@erdani.org)'s article
>> MLT wrote:
>>> One needs something like a lazy semi-infinite range, that only calls
>>> a certain function when it reaches an unexplored part.
>> That's a great abstraction, but we can't afford to impose that to
>> everybody. There must be an abstraction for a one-pass go through an
>> arbitrarily long stream.
>> Anyhow, I decided to change ranges as follows:
>> a) Input:
>> bool empty();
>> ref T popNext();
>> b) Output:
>> void putNext(T);
>> c) Forward:
>> bool empty();
>> ref T front();
>> void popFront();
>> The function ref T popNext() is a nonmember that accepts any forward
>> range and uses front() and popFront(). Of course, a forward range is
>> welcome to implement popFront as a member if it so wishes. The function
>> putNext() overwrites front() and then calls popFront.
>> d) Bidirectional:
>> bool empty();
>> ref T front();
>> void popFront();
>> ref T back();
>> void popBack();
>> popNext, putNext apply as for forward ranges.
>> e) Random
>> bool empty();
>> ref T front();
>> void popFront();
>> ref T back();
>> void popBack();
>> ref T opIndex(uint n);
>> void opIndexAssign(T, uint n);
>> popNext, putNext apply as for forward ranges. We need to fix the
>> opIndexAssign mess.
>> Andrei
>
> (Bangs head against desk.) Sorry. Didn't see the part about the non-member
> popNext(), though this would require Walter to make extension methods work for
> structs, which should probably happen anyway.
I should learn to never post before reading all messages...
Andrei
| |||
May 21, 2009 Re: "the last change" for ranges | ||||
|---|---|---|---|---|
| ||||
Posted in reply to Andrei Alexandrescu | "Andrei Alexandrescu" <SeeWebsiteForEmail@erdani.org> wrote in message news:gv2hj8$k1n$1@digitalmars.com... > dsimcha wrote: > > Consider: > > struct R > { > bool empty(); > ref int front(); > void popFront(); > } > > ref int popNext(ref R fwdRange) > { > auto result = & fwdRange.front(); > fwdRange.popFront; > return *result; > } > > void main() > { > R r; > int x = r.popNext; > } > > This should work, I just noticed with surprise it doesn't. It's a bug, specifically bug 3015: > > http://d.puremagic.com/issues/show_bug.cgi?id=3015 > I thought that was only supposed to work for arrays. Has that changed? If so, what's the new rule? | |||
Copyright © 1999-2021 by the D Language Foundation
Permalink
Reply