Uh oh!
There was an error while loading. Please reload this page.
Remove elements from std.ArrayList while iterating over it - #3037
Remove elements from std.ArrayList while iterating over it#3037Tetralux wants to merge 12 commits into
Conversation
Tetralux
commented
Aug 10, 2019
Huh. That's weird. This should have been a compile error:
|
Tetralux
commented
Aug 10, 2019
Note that |
DutchGhost
commented
Aug 11, 2019
Should't the normal iterator be like it currently is, and then add a removable iterator? Changing the current iterator breaks everywhere its used, while adding a new one doesn't. Also, perhaps one could consider taking a function in the initialization function of this removing iterator, that has a signature like |
Tetralux
commented
Aug 11, 2019
@DutchGhost If you specifically want only the old behavior---you don't want the user to be able to modify the elements---then you'd call As to the predicate idea, I think the way it is here is clearer.
I think the way I have it right now is more flexible, and perfectly clear. I also asked in the IRC while making this PR as to whether I should make the current situation the default or not (i.e: |
DutchGhost
commented
Aug 11, 2019
I expect an Iterator function to just simply return me an iterator without fancy things. |
data-man
commented
Aug 11, 2019
But decreases performance. |
Tetralux
commented
Aug 12, 2019
That is the case with PR. It only does "fancy" things (I'd argue that being able to remove an element while iterating over it isn't very fancy) when you call the procedures. It otherwise doesn't do anything different.
What you're describing is exactly what this PR does. You literally call the "fancy" procedure alongside iterating.
I have a patch for that. 😉 |
Did some rudimentary performance test of [debug mode] using this branch and compared to master.
Of course, benchmarking is a dark art, so... pinch of salt, and all that. conststd=@import("std");
constwarn=std.debug.warn;
consttime=std.time;
pubfnmain() !void {
vara=std.heap.direct_allocator;
varl=std.ArrayList(usize).init(a);
varr=std.rand.DefaultPrng.init(32); // Xoroshiro128vari: usize=0;
while (i<10000000) : (i+=1) {
varx=r.next();
tryl.append(x);
}
varj: usize=0;
varaverage_sum: f64=0;
while (j<100) : (j+=1) {
vartimer=trytime.Timer.start();
varitr=l.iterator();
varsum: usize=0;
while (itr.next()) |e| {
sum+%=e;
}
vartook=timer.read();
warn("took {} ns\n", took);
average_sum+=@intToFloat(f64, took);
}
varaverage_time=@floatToInt(u64, std.math.round(average_sum/@intToFloat(f64, j+1)));
warn("average {}\n", average_time);
} |
DutchGhost
commented
Aug 12, 2019
@Tetralux, what I ment was that if I wanted an iterator that is able to do fancy thing, I'd call a method on the ArrayList that suggests the iterator I'm getting from it can do more than just iterating. With your change, the default iterator would do more than just iterating, which you pay for, even if its just a little. You might argue that you moved the old one to a new struct and new method, but then already existing code has to opt out of your change, and call something new, while if you only added ".removingIter" (or whatever name you'd like) to the list of methods, that isn't the case. So,
|
There was a problem hiding this comment.
Avoid this branch by putting the decrement into the remove function.
Then this can be a non-branching it.removed = false.
There was a problem hiding this comment.
Interestingly, in debug mode at least, what you suggest is actually slower than the way it currently is.
There was a problem hiding this comment.
The rudimentary test above seems to indicate that if you remove the branch, you lose around >5%.
I did move the decrement out of next though. Good call on that.
|
Tetralux
commented
Aug 12, 2019
(Updated comment.) |
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Had some trouble figuring out how to make the semantics that removal procedures always remove the last element returned from
|
Tetralux
commented
Aug 16, 2019
Actually scratch that. |
daurnimator
commented
Aug 17, 2019
I'm confused. The change should have just been erroring in remove if |
Tetralux
commented
Aug 17, 2019
@daurnimator What you suggest is the first thing I tried. But as explained above, this is faster for [release-safe] and [debug mode]. |
Tetralux
commented
Aug 18, 2019
Rebased to master. |
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
… iterating std.ArrayList.
Add tests for removing the first element
…n 1% of normal removal procedures.
This makes removal procedures alwayts remove the last element returned from next. Before, they'd remove the first element if you called for removal before any calls to next.
e27d1bc to
ddccf82Compare
You can now remove an element from a
std.ArrayList, while iterating over one.By ordered-removal:
By swap-removal:
If you only have a
*const std.ArrayList, there's.iteratorConst(), which gives you an iterator with only the old behavior - i.e: which cannot remove elements.