[ruby-core:96558] [Ruby master Feature#16446] Enumerable#take_*, Enumerable#drop_* counterparts with positive conditions
From:
sawadatsuyoshi@...
Date:
2019-12-28 17:59:43 UTC
List:
ruby-core #96558
Issue #16446 has been updated by sawa (Tsuyoshi Sawada). I understand some people thinking that adding all these methods is doing too much. I came up with an idea of using just one method: `Enumerable#cut`. It works like `String#partition` when it does not take an argument (but unfortunately, the method name `Enumerable#partition` is already used for a different purpose). ```ruby enum.cut(&:zero?) # # => [[1, 1], [0], [3, 3, 0, 5, 5]] ``` It would take optional keyword argument `:take` or `:drop`. ```ruby enum.cut(take: :left, &:zero?) # => [1, 1] enum.cut(drop: :left, &:zero?) # => [0, 3, 3, 0, 5, 5] enum.cut(drop: :right, &:zero?) # => [1, 1, 0] enum.cut(take: :right, &:zero?) # => [3, 3, 0, 5, 5] ``` ---------------------------------------- Feature #16446: Enumerable#take_*, Enumerable#drop_* counterparts with positive conditions https://bugs.ruby-lang.org/issues/16446#change-83491 * Author: sawa (Tsuyoshi Sawada) * Status: Open * Priority: Normal * Assignee: * Target version: ---------------------------------------- #16441 led me to think about the issue more generally. When we want to split a series of iterations by the first element that satisfies (or dissatisfies) a condition, we have three factors to consider. (1) Whether we want the condition to work **negatively** or **positively** (2) Whether we want the first element to satisfy (or dissatisfy) the condition to be included in the **left** side or the **right** side of the split (3) Whether we want the **left** side or the **right** side in the returned output This leads us to eight possible combinations to consider. ```ruby enum = [1, 1, 0, 3, 3, 0, 5, 5].to_enum ``` | |(1)|(2)|(3)|method|example| |--|--|--|--|--|--| |1|negatively|left|left|`take_while`|`enum.foo1(&:nonzero?) # => [1, 1]`| |2|negatively|left|right|`drop_while`|`enum.foo2(&:nonzero?) # => [0, 3, 3, 0, 5, 5]`| |3|negatively|right|left||`enum.foo3(&:nonzero?) # => [1, 1, 0]`| |4|negatively|right|right||`enum.foo4(&:nonzero?) # => [3, 3, 0, 5, 5]`| |5|positively|left|left||`enum.foo5(&:zero?) # => [1, 1]`| |6|positively|left|right||`enum.foo6(&:zero?) # => [0, 3, 3, 0, 5, 5]`| |7|positively|right|left||`enum.foo7(&:zero?) # => [1, 1, 0]`| |8|positively|right|right||`enum.foo8(&:zero?) # => [3, 3, 0, 5, 5]`| Proposal #16441 asks for a method that corresponds to case 3 in the table above, but I think that would make the paradigm messy unless case 4 is also implemented. Either cases 3 and 4 should both be implemented, or both not. Actually, the current proposal is not about cases 3 and 4. I would leave that to #16641. In many use cases (including the first example in #16641), we want to detect the "marker element" by which we split the iterations. In the cases above, that can be the element `0`. In such use cases, it is more natural to describe the condition in positive terms (i.e., `zero?`) rather than negative terms (i.e., `nonzero?`). (And in other use cases, it might be the other way around.) So I would like to propose methods that correspond to cases 5, 6, 7, 8 above. Naming of the methods should be done systematically. As a candidate, I came up with the following: ||method| |--|--| |5|`take_before`| |6|`drop_before`| |7|`take_upto`| |8|`drop_upto`| -- https://bugs.ruby-lang.org/ Unsubscribe: <mailto:ruby-core-request@ruby-lang.org?subject=unsubscribe> <http://lists.ruby-lang.org/cgi-bin/mailman/options/ruby-core>