We need to rethink remove in std.container
Steven Schveighoffer
schveiguy at yahoo.com
Wed Feb 23 06:51:59 PST 2011
On Wed, 23 Feb 2011 08:33:03 -0500, Andrei Alexandrescu
<SeeWebsiteForEmail at erdani.org> wrote:
> On 2/22/11 8:41 AM, Steven Schveighoffer wrote:
>> On Tue, 22 Feb 2011 09:23:00 -0500, Andrei Alexandrescu
>> <SeeWebsiteForEmail at erdani.org> wrote:
>>> This may as well be The Great Unification that was needed to converge
>>> std.container with dcollections in a harmonious whole.
>>
>> I'm not sure what to think of this ;) Is this a "oh what could have
>> been" statement or a "let's reopen negotiations" statement?
>>
>> Note, I would love for dcollections to be mainstream Phobos, but I have
>> accepted the current reality and am fine with the way things are now
>> too.
>
> Absolutely, and your collaboration on e.g. RedBlackTree and others is
> very much appreciated.
>
> The way I was thinking is, there would a lot of convergence if
> dcollections wiped the notion of "cursor" and replaced it with takeOne
> throughout. One obvious objection to the range/cursor design is there
> are two abstractions to deal with instead of one. I had the intuition
> that ranges should be enough, but was not quite able to substantiate it
> fully until just about now. With take, takeExactly, and takeOne, it
> seems that indeed there is only need for the range interface, and that
> particular range types can replace cursors.
I don't think this is possible. When passing a range to a container for
removal, the container needs to access the underlying implementation (e.g.
pointer to tree node) in order to immediately know what element is being
removed. The takeOne range you propose does not allow this, you can't get
at the underlying range. Even if you could, only takeOne!(myrangetype)
would work, which leaves us right back at supporting a small number of
specific ranges.
In addition, a cursor points to one element, a range points to multiple
elements. *conceptually* takeOne is the same, but it still stores the
range internally, so it's actually more expensive than a range to pass
around (additional _empty member).
I was thinking more along the lines of ranges being able to generate
cursors if it makes sense (i.e. base range is a range of the container).
Dcollections already supports this with the two accessors begin() and
end(). We might also need a last() which gets the last valid element in
the range. This would be akin to back().
Here is what I was thinking that can help with unifying interface. I'll
just show you as a code snippet:
void remove(C)(C c) if (is(C == mycursortype))
{
// since we know the structure of c, we can use it to remove the
element from the container
}
void remove(R)(R r) if (is(R == myrangetype))
{
// possibly can be optimized
}
void remove(R)(R r) if (!is(R == myrangetype) && isInputRange!R &&
is(typeof(r.begin) == mycursortype))
{
while(!r.empty)
{
auto c = r.begin;
r.popFront();
remove(c);
}
}
If wrapper ranges can "forward" the begin and end functions through their
API, then you can construct arbitrary ranges that will be accepted as
parameters to remove. We could potentially add forwarding functions to
all wrapper ranges, or we could pick ones that make the most sense (like
take).
In addition, dcollections (and any collection that can verify its
endpoints and cursor membership, SList obviously cannot) allows slicing
based on cursors in a limited fashion. Therefore, this allows
constructing ranges from cursors if you have the original container.
As an example of what this could allow (assuming retro and take forward
the begin(), end() and last() methods):
container.remove(take(retro(container[]), 5)); // remove the last 5
elements from the container.
>
> I realize that there are other major differences in design between
> dcollections and std.container, probably the largest one being the use
> of a hierarchy vs. free-floating types, so probably full unification is
> still not possible or quite remote. Nevertheless, keeping the
> communication channels open would make both libraries better and all of
> us a bit wiser.
As I've said in the past, the interface hierarchy is not critical at the
moment because D has very little shared library support. I'm willing to
remove the interfaces as long as the types remain classes (which I believe
you are more agreeable with lately). This allows the interfaces to be
added on later if it makes sense.
The cursor concept has to stay though.
-Steve
More information about the Digitalmars-d
mailing list