Add shift(): wrapper for front and popFront#4010
Add shift(): wrapper for front and popFront#4010wilzbach wants to merge 1 commit intodlang:masterfrom
Conversation
|
This comes up from time to time, but the main issue is that I think it's also a small issue in practice, as higher level range algorithms can take care of calling the range primitives correctly. |
|
I think I prefer the names |
@JakobOvrum: Can't we then duplicate the front value as this method is really about convenience.
Wouldn't it be a bit confusing to have Any opinion from @MartinNowak? |
|
@greenify Of course we can duplicate Having said that, though, transient ranges aren't very common, so most of the time this should work. And it does indeed simplify certain kinds of code, e.g., recursive descent parsers, where the pattern |
|
The reality of the matter is that ranges with a transient front tend to break if you do much more than use As for the name, I think that |
I renamed |
| r.popFront(); | ||
| return e; | ||
| } | ||
|
|
There was a problem hiding this comment.
Examples should come from ddoc-ed unit tests, not in documentation. All you have to do is add /// to the previous line
|
thanks a lot @JackStouffer :)
Thanks! I didn't know that - I added the
Because otherwise (as far as I understood it) the pointers to the range will be copied and popping will only modify the copy - see also e.g.
I could only find one place in -> Added & squashed. |
The secondary unit tests don't add any extra value, so I don't think that they should be ddoc-ed.
They were added a while ago. All of the functions in Phobos should have them, but a lot haven't been updated yet. These functions could also use the following contract |
Okay.
I thought that |
|
LGTM |
|
Please add links to |
|
@quickfur added to header in |
|
Hmm, I guess |
|
LGTM |
| auto frontPop(Range)(ref Range r) | ||
| if (isInputRange!Range) | ||
| in | ||
| { |
There was a problem hiding this comment.
The assertion message is not useful (the r). Either it must be removed or changed for more value added.
I suggest assert(!r.empty, "empty range passed in " ~ __PRETTY_FUNCTION__);�. With pretty function, the type of the argument is also written.
same remark for backPop input contract.
|
@bbasile wow thanks - I didn't know that special keyword existed! |
| auto backPop(Range)(ref Range r) | ||
| if (isBidirectionalRange!Range) | ||
| in | ||
| { |
There was a problem hiding this comment.
Shouldn't the message be "... passed to" rather than "passed in"?
038774b to
01abe82
Compare
std/range/primitives.d
Outdated
| Next element of the input range | ||
| */ | ||
| auto shift(Range)(ref Range r) | ||
| if (isInputRange!Range |
There was a problem hiding this comment.
Not a typo - popFrontN does the attribute inference for us - just popFront wouldn't work due to the difference between attribute and function.
We could use a private alias if that's too cryptic.
There was a problem hiding this comment.
We could use a private alias if that's too cryptic.
Updated to use one - it's more readable for the user this way ;-)
Wohoo - apparently I am not the only one missing this function.
done.
done.
Removed and added a warning about transient ranges.
Good idea, what do I other people think? |
Considering that it is very unlikely that D would be the first language where a user encounters such a function, I think it would be more useful to use the names existing in other languages, barring any strong reasons such as conflicts (which I don't see). |
std/range/primitives.d
Outdated
| } | ||
|
|
||
| /// | ||
| @safe pure unittest |
There was a problem hiding this comment.
Please add @nogc and nothrow. For @nogc you need to make the following change:
int[3] arr = [1, 2, 3];
int r[] = arr[];
There was a problem hiding this comment.
Thanks & added.
Even though I think this makes the first test for the user a bit uglier to read.
There was a problem hiding this comment.
Thanks.
Yes, but it shows an interesting (for newbies) effect: only the slice gets modified and not the original array, which IIRC is not the case in other languages like JS.
Maybe it's worth adding assert (arr.equals(4.iota)) at the end.
There was a problem hiding this comment.
Maybe it's worth adding assert (arr.equals(4.iota)) at the end.
done ;-)
| Next element of the input range | ||
| */ | ||
| auto shift(Range)(ref Range r) | ||
| if (isInputRange!Range && hasNothrowPopFront!Range) |
There was a problem hiding this comment.
Nitpick: please indent the if with 4 spaces. Ditto for shiftBack.
There was a problem hiding this comment.
hmm after having a short look at the other template constraints in primitives , they also don't seem to use indention
There was a problem hiding this comment.
I'm not sure where you looked. The only exception is putChar:
$ pcregrep -MHn "\)\nif" range/primitives.d
range/primitives.d:252:correct primitive: $(D r.put(e)) if $(D R) defines $(D put), $(D r.front = e)
if $(D r) is an input range (followed by $(D r.popFront())), or $(D r(e))
range/primitives.d:348:private void putChar(R, E)(ref R r, E e)
if (isSomeChar!E)
$ pcregrep -MHn "\)\n if" range/primitives.d
range/primitives.d:1596:auto walkLength(Range)(Range range)
if (isInputRange!Range && !isInfinite!Range)
range/primitives.d:1610:auto walkLength(Range)(Range range, const size_t upTo)
if (isInputRange!Range)
range/primitives.d:1667:size_t popFrontN(Range)(ref Range r, size_t n)
if (isInputRange!Range)
range/primitives.d:1703:size_t popBackN(Range)(ref Range r, size_t n)
if (isBidirectionalRange!Range)
range/primitives.d:1798:void popFrontExactly(Range)(ref Range r, size_t n)
if (isInputRange!Range)
range/primitives.d:1814:void popBackExactly(Range)(ref Range r, size_t n)
if (isBidirectionalRange!Range)
$ pcregrep -MHn "\)\n if" range/package.d | wc -l
86
$ pcregrep -MHn "\)\n if" algorithm/* | wc -l
170
~/dev/repos/dlang/phobos/std
$ find -type f -name "*.d" -exec pcregrep -MHn "\)\nif" {} \; | wc -l
342
~/dev/repos/dlang/phobos/std
$ find -type f -name "*.d" -exec pcregrep -MHn "\)\n if" {} \; | wc -l
1278
There was a problem hiding this comment.
Andrei seems to prefer the current style.
There was a problem hiding this comment.
You mean Andrei prefers the less common style. OK, can't argue with that.
There was a problem hiding this comment.
The only exception is putChar
> grep "^if (" primitives.d
if (r.empty) {} // can test for empty
if (isSomeChar!E)
if (isInputRange!Range && hasNothrowPopFront!Range)
if (isBidirectionalRange!Range && hasNothrowPopBack!Range)
if (!isNarrowString!(T[]) && !is(T[] == void[]))
if (isNarrowString!(C[]))
if (!isNarrowString!(T[]) && !is(T[] == void[]))
if (isNarrowString!(T[]))
if (!isNarrowString!(T[]) && !is(T[] == void[]))
if (!isNarrowString!(T[]))
There was a problem hiding this comment.
You mean Andrei prefers the less common style. OK, can't argue with that.
Guys, I don't really care about this - the main point here is to get shift into Phobos :)
Btw we should just enable some code style tool like DScanner for Phobos and agree once on one of both ;-)
cc51776 to
2c8cd84
Compare
std/range/primitives.d
Outdated
| import std.algorithm.comparison: equal; | ||
| import std.range: iota; | ||
| // original array stays unmodified | ||
| assert((cast(int[]) arr).equal(iota(1, 4))); |
There was a problem hiding this comment.
How about: assert(arr[].equal(iota(1, 4))); ?
There was a problem hiding this comment.
thanks - that's a lot nicer :)
|
Overall LGTM, but I think we have to do something about ranges with non-copyable elelements to improve the API symmetry:
|
Couldn't we call |
|
This won't work with elements can be both moved and copied - e.g. copying a
|
|
If we don't come up with a good name, we can always add a template parameter: But using |
I think this is quite a bit less common so it's less clear a wrapper is needed for that. |
|
Yes, maybe for scripts you are right. However when your application is D is a great systems programming language, but unfortunately Phobos was not written and is not actively developed with Ranges are great in helping you avoid allocations and unnecessary work, but that works until your application grows large enough that resource management becomes unavoidable. Then suddenly the lacking move support becomes a deal-breaker. We can't leave things as they are. We need to start somewhere. |
|
I'm 75/25 against this.
The way I look at it there's just too much cons to it. There is real value to the function, but it's weighed against powerful issues. I do see a few positives coming out of this:
I'll close this, but don't take it as standoffish. If anyone finds a pro argument that they think is killer, please reopen. |
|
What about enabling Possible alternative namings Somewhat related: http://forum.dlang.org/post/dafmzroxvaeejyxrkbon@forum.dlang.org |
|
@nordlow thanks for your input. I'd say by the point the explanation is 5x the size of the two-liner, it's better to do without. Never mind |
Hey all,
I am quite new to D and I was wondering why there is no convenience wrapper which combines
frontandpopFront. I know there istakeOne(which doesn't modify the source) anddropOne(which will return the resulting array after dropping one element).I think the best example of such a method in other languages is the
nextmethod in Python, thus the naming. However feel free to suggest other names. Other ideas would beshift,takeFirst,removeFirst,shiftFirst.If it makes sense to you I am happy to add the opposite method (
takeLast,removeLast,shiftLast).Pros:
Cons:
As mentioned above I am just starting with D, so let me know if there is a better way to write this PR or you would add more checks (afaik
frontandpopFrontwill throw an Assertion Error if the range is empty, so this behavior should be induced).I initially asked this question at the DLang forum, which lead me to submit this PR.
https://forum.dlang.org/post/orcjkabkcffueduqkyml@forum.dlang.org