On Sun, 16 May 2010 02:24:55 -0400, Don <nos...@nospam.com> wrote:

Steven Schveighoffer wrote:
Currently, D supports the special symbol $ to mean the end of a container/range. However, there is no analogous symbol to mean "beginning of a container/range". For arrays, there is none necessary, 0 is always the first element. But not all containers are arrays. I'm running into a dilemma for dcollections, I have found a way to make all containers support fast slicing (basically by imposing some limitations), and I would like to support *both* beginning and end symbols.
 Currently, you can slice something in dcollections via:
 coll[coll.begin..coll.end];
I could replace that end with $, but what can I replace coll.begin with? 0 doesn't make sense for things like linked lists, maps, sets, basically anything that's not an array. One thing that's nice about opDollar is I can make it return coll.end, so I control the type. With 0, I have no choice, I must take a uint, which means I have to check to make sure it's always zero, and throw an exception otherwise. Would it make sense to have an equivalent symbol for the beginning of a container/range? In regex, ^ matches beginning of the line, $ matches end of the line -- would there be any parsing ambiguity there? I know ^ is a binary op, and $ means nothing anywhere else, so the two are not exactly equivalent. I'm not very experienced on parsing ambiguities, but things like ~ can be unambiguous as binary and unary ops, so maybe it is possible.
 So how does this look:  coll[^..$];
 Thoughts? other ideas?
 -Steve

If we were to have something like this (and I'm quite unconvinced that it is desirable), I'd suggest something beginning with $, eg $begin.

This would be better than nothing.

But, it seems to me that the slicing syntax assumes that the slicing index can be mapped to the natural numbers. I think in cases where that's not true, slicing syntax just shouldn't be used.

slicing implies order, that is for sure. But mapping to natural numbers may be too strict. I look at slicing in a different way. Hopefully you can follow my train of thought.

dcollections, as a D2 lib, should support ranges, I think that makes the most sense. All containers in dcollections are classes, so they can't also be ranges (my belief is that a reference-type based range is too awkward to be useful). The basic operation to get a range from a container is to get all the elements as a range (a struct with the range interface).

So what if I want a subrange? Well, I can pick off the ends of the range until I get the right elements as the end points. But if it's possible, why not allow slicing as a better means of doing this? However, slicing should be a fast operation. Slicing quickly isn't always feasible, for example, LinkList must walk through the list until you find the right element, so that's an O(n) operation. So my thought was to allow slicing, but with the index being a cursor (i.e. pointer) to the elements you want to be the end points.

Well, if we are to follow array convention, and want to try not to enforce memory safety, we should verify those end points make sense, we don't want to return an invalid slice. In some cases, verifying the end points are in the correct order is slow, O(n) again. But, you always have reasonably quick access to the first and last elements of a container, and you *know* their order relative to any other element in the container.

So in dcollections, I support slicing on all collections based on two cursors, and in all collections, if you make the first cursor the beginning cursor, or the second cursor the end cursor, it will work. In some cases, I support slicing on arbitrary cursors, where I can quickly determine validity of the cursors. The only two cases which allow this are the ArrayList, which is array based, and the Tree classes (TreeMap, TreeSet, TreeMultiset), where determining validity is at most a O(lgN) operation.

Essentially, I see slicing as a way to create a subrange of a container, where the order of the two end points can be quickly verified.

auto dict = new TreeMap!(string, string); // TreeMap is sorted

...

auto firstHalf = dict["A".."M"];

(You say that slicing using anything besides natural numbers shouldn't be used. You don't see any value in the above?)

But "A" may not be the first element, there could be strings that are less than it (for example, strings that start with _), such is the way with arbitrary maps. So a better way to get the first half may be:

auto firstHalf = dict[dict.begin.."M"];

What does the second half look like?

auto secondHalf = dict["M"..dict.end];

Well, if we are to follow array convention, the second half can be shortcutted like this:

auto secondHalf = dict["M"..$];

Which looks and reads rather nicely. But there is no equivalent "begin" shortcut because $ was invented for arrays, which always have a way to access the first element -- 0. Arbitrary maps have no such index. So although it's not necessary, a shortcut for begin would also be nice.

Anyways, that's what led me to propose we have some kind of short cut. If nothing else, at least I hope you now see where I'm coming from, and hopefully you can see that slicing is useful in cases other than natural number indexes.

-Steve

Reply via email to