Added byadgar (Michael Edgar)almost 15 years ago. Updatedover 8 years ago.
Description
=begin
A very common operation on sets is to take an arbitrary element from them at O(1) cost. I know typically
it's suggested that library additions go out as a gem to see community review. However, to me it seems
to be a glaring omission to lack such an operation on a built-in, fundamental data structure, so I've
gone straight to the bug tracker.
I wasn't too sure which method names to use as I've often heard "take" in lieu of "pop," so I just used the
names Wikipedia uses. Consider them flexible. "Pick" selects an arbitrary element, and "pop" selects and
deletes it.
I've provided a very simple patch that implements the necessary behavior. Thoughts?
=end
Files
| pick_pop.diff(1.06 KB)pick_pop.diff | implementation and tests for Set#pick and Set#pop | adgar (Michael Edgar), 04/05/2011 08:10 AM |
=begin
Hi.
There is no difference between yourSet#pick and the existingSet#first. As such, there is no need forpick.
One can get the effect ofSet#pop by calling the existingSet#delete with the result ofSet#first (with the same O(1) efficiency).
I find problematic that a method calledpop would take the first element; it could be renamed toshift, say, or take the last element instead. More importantly, though, I am not convinced that this functionality is that frequently used.
=end
=begin
The ruby-core e-mail I sent appeared to not attach to the issue on Redmine. I apologize for the noise.
The existence ofSet#first is an artifact of Set including Enumerable and the very idea of "first" on a Set is an odd notion. A set by itself does not have ordering. I have implemented it to pick the first element because it is simplest, but someone calling "pick" on a set does not care which element it is. If one would prefer implementing it as an alias to first, I'd be fine with that.
On a personal note to emphasize why I find this issue compelling: one reason I enjoy using Ruby because often my code reads just as I would describe it to a colleague - reading "element = set.first" is not something many would agree is a sensible way to describe the act of picking an arbitrary element from a mathematical set.
Yes, one could implement #pop with #delete and #first, which again relies on a notion of ordering in a set. One can implement #proper_subset with #subset and a comparison, as can one implement #proper_superset with #superset and a comparison. Yet they are in the Set class because they are fundamental operations. #add? and #delete? can also be implemented with #add and #include?/#delete and #include? respectively, yet they are included in the Set class because they are useful and common operations on a set.
Ifpop is too close the Array method of the same name, I understand the confusion, but I don't get why picking first/last makes a difference. Anyone asking to remove an arbitrary element from a Set surely doesn't care where it lies in an underlying hash table. I am surprised one would argue that the Set class rarely needs the notion of selecting an element from it. Rarely am I presented with an algorithm using sets that does not require simply getting an element or picking one out of a set.
=end
=begin
#pop is often associated to stack operations, which implies an order. Unless a better name is found, isn't set.delete(set.take) enough ?
#take can be an alias of #first but I'm not sure if Enumerable should be included in Set in the first place. In my POV, Enumerable is for elements that have a particular order.
=end
Any update on this? It seems like a reasonable hole to fill in Ruby 1.9.3.
I would expectArray#pick to work too. Which then leads one to think Array might support the equivalent ofSet#pop as well. But since Array already has#pop it's not really a good idea to reuse same term, although in the case of Set,#pop might be an alias for such method.
So what might such methods be called then? Well, if we look at hand-dandyfacets/random:
https://github.com/rubyworks/facets/blob/master/lib/standard/facets/random.rb
Looks like it uses #at_rand/#at_rand! and #pick/pick!, where #pick has the additional option of returning a random subset if a size is given as argument.
As marcandre says, thispick method does exactly whatfirst does, and I'm not sure ifpop would be an important primitive method.
Array now hassample that picks a random element(s) from an array without deleting it which may or may not be the function you want, but I can't think of a way to randomly pick a key from a hash at O(1) cost, at least with a pure ruby implementation.