We need to rethink remove in std.container

Jonathan M Davis jmdavisProg at gmx.com
Mon Feb 21 21:27:18 PST 2011


On Monday 21 February 2011 21:04:47 %u wrote:
> > remove takes a range type which is the range type for the
> 
> container that it's on. That makes sense. It's obviously not going
> to be able to a take an arbitrary range and remove that from itself.
> How would it know which elements in that range corresponded to which
> elements in itself - especially when it could be a range which skips
> elements or something similar? So, _that_ part makes sense.
> 
> I'm wondering, what was the reason for making a "remove range X from
> Y" method in the first place? To me, that's not a "remove"
> operation; if anything, it's the "removeAll" operation, but I'd
> actually call it "subtract" (because it's the set subtraction
> operation... although there can be duplicates).
> 
> If we change remove to only take in a single element, wouldn't its
> implementation then be trivial? (For a range, just return a filter
> that removes the element. For a container, actually remove the
> element. For ranges that also behave like containers -- like arrays
> -- I have no idea.)
> 
> Does this sound like a good idea?

Removing a range is not necessarily related to removing an element with a 
particular value. Sure, a method could be added to the various container types 
which takes a value and removes the first instance of that value from the 
container, but that's not what remove is necessarily trying to do. It's like 
erase in C++'s standard library which takes two iterators. There are plenty of 
situations where removing a range makes sense. The odd bit with remove in Phobos 
vs erase in the STL is that with iterators, you can give a single iterator to 
indicate a single element whereas you can't do that with a range. So, remove 
takes a range, and if you want to remove a single element, that range only has 
one element in it. It's a bit weird, but that's fine.

The typical way to remove an element in the STL is to use find to find an element 
and then erase to remove it. remove in std.container is doing the same thing. 
The problem is that you can't give the result of find to remove, because instead 
of a single iterator, find gives you a whole range, and you probably don't want 
to remove that whole range. You generally either want to remove the first element 
or some set of elements at the front of the range. So, you need to able to 
remove the other elements from the range so that you can give that range to the 
remove method on the container. That's fine in principle, but since so many of 
the range-based algorithms give you a new type back and you need the exact type 
that that container uses for its ranges, it's very hard to get a range of the 
correct type with the correct elements in it.

So, while find will give you the beginning of the range you want, there's not an 
easy way to remove the end of that range. Functions like take and takeExactly 
return new types, so they don't work. The more I look at it, the more I'm 
convinced that we really need to add a primitive to forward ranges that returns 
the first n elements of that range. Without that, I don't see how you can get a 
range of the correct type with only those elements in it unless it's also a 
bidirectional range, which some ranges (like SList's range) aren't.

- Jonathan M Davis


More information about the Digitalmars-d mailing list