On Monday, 10 February 2014 at 21:47:35 UTC, Ty Overby wrote:
I have to be missing something really basic. It couldn't possibly be this hard to remove an element from an array.

I think the correct solution is:

systems.linearRemove(systems[].find(system)[0 .. 1]);

Usually containers have an overload of *[Rr]emove that takes `Take!Range`, allowing:

systems.linearRemove(systems[].find(system).takeOne());

std.container.Array not having overloads for `Take!Range` looks like an oversight.

To the uninitiated this can look ridiculous, but hear me out first.

First of all, the unary slice operation (the empty brackets: []) is the container primitive to get a range spanning the entire container. It is arguably the most fundamental container primitive, and is crucial when using other container primitives, as they often take range parameters. IMO, it would be a nice compiler enhancement to raise a tailored error message in cases where [] is forgotten (probably non-trivial to implement, though).

With that out of the way: in D, both ranges and containers follow the principle that primitives are subject to restrictions on algorithmic complexity to avoid the quadratic complexity minefield that the Java approach heralds through deceptive abstraction of linear algorithms (of which ArrayList/List/Collection.remove is a good example). Therefore, a primitive equivalent of Java's List/Collection.remove is not welcome in D.

Often when linear search is required for this kind of operation, the user has chosen the wrong data structure, so the absence of deceptive abstractions from the core API is *usually* a non-issue. Of course, sometimes linear search is perfectly justified, especially when it comes to (smallish) arrays, or code that is only run very rarely. In these cases, the equivalent D code using range algorithms (shown above) has advantages; we can glean two important artefacts: a) linear search is at play (because of `find`), and b) only the first element found is removed. The obvious disadvantage is that writing such code in the first place requires a relatively high degree of familiarity with Phobos containers and ranges. That said, it's probably offset by the fact that when you *do* start picking up Phobos' generic algorithms and primitives, you can apply them everywhere - learning the API of a new container is just a matter of glancing over what primitives it supports. Better documentation - such as adding examples - is definitely in order to ease this learning, though.

Reply via email to