-
Notifications
You must be signed in to change notification settings - Fork 3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fold over queues #2791
Fold over queues #2791
Conversation
62d4bf0
to
af098c5
Compare
Thanks for the PR! We'll need to run it past the technical board (next week) as it changes an interface, but I think it looks great. |
You mean because of the |
Yes, though we'll discuss |
Nice, I would definitely have used I am not convinced about the return type for |
In that case, @josevalim & @Maria-12648430, maybe write a new @Maria-12648430 - Note that your change to optimize the code handling of Your change optimizes away external calls to Side note: returning A question for decision: would we need both |
I pondered the same and my conclusion was that if there is ever a need for a “reverse fold”, it could be called “fold_r”, which aligns well with the other functions in the module. WDYT? |
You may want to consider the need for a separate function (possibly in a separate pull request) that would avoid processing the whole queue. I have found it necessary, due to testing of the time taken in |
@josevalim: |
@okeuday: A napkin calculation of a possible Would one want both a |
@RaimoNiskanen I needed the predicate fun to access a tuple element but the |
Oh, quite a response :)
Ok, I second that. So, the filtermap function may return |
Ah, got that wrong then.
Ok, I'll add it back.
That would be obsoleted with a separate |
TBH, I had both
Decided then? :) |
I need something like that every now and then, too. Maybe later, in a seperate PR :)
Yes
Hm, I would argue that it is not guaranteed that an item is unique, at least not from the perspective of the queue. I would suggest a function (or two) that removes the first element matching the condition (either predicate function or exact match), then stops traversing further. The front list can be traversed without reversal, so best case would be if the element is in that list (ie, near the front). The rear lists has to be reversed to find the first element (instead of the last), so that would be a worse case. Worst case is if no element is found, which means full traversal plus a reversal in the middle. A more general version of this would be something like |
Another function that could be handy would be (Probably material for another PR, anyway ;)) |
Obsolete or not, it would still be so that |
I agree. And I think fold_r can wait for its demand, since |
I meant that my addition of |
I'll see ;) |
More functions in |
Ouch! Of course, I did not think of that! I did some coding in my head so these functions maybe cannot even be compiled, and may be incorrect, but anyway... This function should traverse the whole list, but only rebuild the cons cells of the start of the list up to the last matching delete_last(Item, [Item | Rest]) ->
case delete_last(Item, Rest) of
false -> Rest;
R -> [Item | R]
end;
delete_last(Item, [X | Rest]) ->
case delete_last(Item, Rest) of
false -> false;
R -> [X | R]
end;
delete_last(_, []) -> false. A similar trick can be used to minimize rebuild of the front list. It returns delete_first(Item, [Item | Rest]) -> Rest;
delete_first(Item, [X | Rest]) ->
case delete_first(Item, Rest) of
false -> false;
F -> [X | F]
end;
delete_first(_, []) -> false. Regarding "remove at most N of these items" I think is an odd bird, and there is also nothing like that in |
Ah, yes. |
@Maria-12648430: That would be a future PR... I think there is a greater need for high level functions in |
Here are two variants more shaped for using a delete_last(Item, [X | Rest]) ->
case delete_last(Item, Rest) of
false ->
case Item of
X -> Rest;
_ -> false
end;
R -> [X | R]
end;
delete_last(_, []) -> false.
delete_first(Item, [X | Rest]) ->
case Item of
X -> Rest;
_ ->
case delete_first(Item, Rest) of
false -> false;
F -> [X | F]
end
end;
delete_first(_, []) -> false. |
Ok, I'll recap:
Agree so far? |
af098c5
to
cf76f3e
Compare
Ok, all done :) A note on |
lib/stdlib/doc/src/queue.xml
Outdated
<p>Calls <c><anno>Fun</anno>(<anno>Item</anno>, <anno>AccIn</anno>)</c> | ||
on successive items <c>Item</c> of <c>Queue</c>, starting | ||
with <c><anno>AccIn</anno> == <anno>Acc0</anno></c>. The queue is | ||
traversed in queue order, i.e. from front to rear. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We do not use such abbreviations in documentation. Write "i.e." as "that is".
@Maria-12648430: We brought this up on our Technical Board, and have a few style comments.
|
I'll do all that on monday, afk atm :) |
If the filter function returns a single-item list or an empty list, it is treated the same way as the respective semantically equivalents of true and false.
A fold function in the queue module removes the need for a conversion to a list in order to achieve the same.
cf76f3e
to
496e490
Compare
@RaimoNiskanen everything done :) Please review again.
I decided to leave it as it is (front list via |
A more general question/request for opinions (especially in view of future additions to the Should it be explicitly mandatory for the queue module to perform any traversals in queue order, at least when predicate functions are involved? It may ( |
My two cents: it should always be done in queue order when user functions are involved, for the possible side effects. |
Merged. Thank you for your pull request! |
You're welcome :) |
queue:fold/3 erlang/otp#2791 queue:all/2, queue:any/2 erlang/otp#2850
The first commit in this PR introduces a
fold/3
function for thequeue
module. Up to now, this had to be done by converting a queue to a list and folding over that, which is both cumbersome and more performance-intensive (O(length(Rear))
for the conversion, andO(length(List))
for the fold over that list) than a fold over the internal lists making up theRear
andFront
, inside thequeue
module (O(len(Queue))
, roughly equivalent to onlyO(length(List))
). The queue is traversed in queue order (front to rear), which is what would be expected.The second commit in this PR addresses a "glitch" in the
filter/2
function, namely that while returningtrue
orfalse
from the filter function is semantically equivalent to returning[Item]
or[]
respectively, the latter generates more garbage. By handling the two special cases "single-element list" and "empty list" the same astrue
andfalse
respectively, this is no longer the case, and the paragraph in the function documentation has been removed accordingly.Also, to align
queue:filter/2
more closely withlists:filtermap/2
(queue:filter/2
is really a filtermap with insert capability), the filter function may return{true, NewItem}
to signal that the current item should be replaced withNewItem
in the result queue.