ruby-core@ruby-lang.org archive (unofficial mirror)
 help / color / mirror / Atom feed
* [ruby-core:96419] [Ruby master Feature#16446] Enumerable#take_*, Enumerable#drop_* counterparts with positive conditions
       [not found] <redmine.issue-16446.20191223082605@ruby-lang.org>
@ 2019-12-23  8:26 ` sawadatsuyoshi
  2019-12-23 15:14 ` [ruby-core:96434] " shevegen
                   ` (6 subsequent siblings)
  7 siblings, 0 replies; 8+ messages in thread
From: sawadatsuyoshi @ 2019-12-23  8:26 UTC (permalink / raw)
  To: ruby-core

Issue #16446 has been reported by sawa (Tsuyoshi Sawada).

----------------------------------------
Feature #16446: Enumerable#take_*, Enumerable#drop_* counterparts with positive conditions
https://bugs.ruby-lang.org/issues/16446

* 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/

^ permalink raw reply	[flat|nested] 8+ messages in thread

* [ruby-core:96434] [Ruby master Feature#16446] Enumerable#take_*, Enumerable#drop_* counterparts with positive conditions
       [not found] <redmine.issue-16446.20191223082605@ruby-lang.org>
  2019-12-23  8:26 ` [ruby-core:96419] [Ruby master Feature#16446] Enumerable#take_*, Enumerable#drop_* counterparts with positive conditions sawadatsuyoshi
@ 2019-12-23 15:14 ` shevegen
  2019-12-23 20:29 ` [ruby-core:96440] " wishdev
                   ` (5 subsequent siblings)
  7 siblings, 0 replies; 8+ messages in thread
From: shevegen @ 2019-12-23 15:14 UTC (permalink / raw)
  To: ruby-core

Issue #16446 has been updated by shevegen (Robert A. Heiler).


I have not thought about the proposal here quite much so far, sorry (and admittedly I may need
to do so  on a fresh morning as I am a bit tired).

Just two brief comments:

- Some of the examples remind me a bit of .select/.reject/.filter. Personally I try
to favour .select whenever possible, largely because I like positive selection/filtering.

- To your statement "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?)".

Yup, completely agree with you there. We may have to include cases that do not have a
clear decisive difference, though, such as .odd? and .even? for numbers, and perhaps
other elements similar to this.

----------------------------------------
Feature #16446: Enumerable#take_*, Enumerable#drop_* counterparts with positive conditions
https://bugs.ruby-lang.org/issues/16446#change-83356

* 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/

^ permalink raw reply	[flat|nested] 8+ messages in thread

* [ruby-core:96440] [Ruby master Feature#16446] Enumerable#take_*, Enumerable#drop_* counterparts with positive conditions
       [not found] <redmine.issue-16446.20191223082605@ruby-lang.org>
  2019-12-23  8:26 ` [ruby-core:96419] [Ruby master Feature#16446] Enumerable#take_*, Enumerable#drop_* counterparts with positive conditions sawadatsuyoshi
  2019-12-23 15:14 ` [ruby-core:96434] " shevegen
@ 2019-12-23 20:29 ` wishdev
  2019-12-23 21:52 ` [ruby-core:96442] " sawadatsuyoshi
                   ` (4 subsequent siblings)
  7 siblings, 0 replies; 8+ messages in thread
From: wishdev @ 2019-12-23 20:29 UTC (permalink / raw)
  To: ruby-core

Issue #16446 has been updated by wishdev (John Higgins).


The answer here is simplicity (declarative) as opposed to wordsmithing.

We need to add an drop_until and take_until to handle 5-8 and drop_while and take_while handle 1-4

All 4 methods get a new parameter "bucket" which determines where the element that triggers the split gets placed :left or :right bucket

Your table looks something like this to me - I apologize but I prefer simpler examples

ary = [-1, 0, 1]


`ary.take_while{ |x| x.nonzero? } # => [-1]`
`ary.take_while(:right){ |x| x.nonzero? } # => [-1]`
`ary.take_while(:left){ |x| x.nonzero? } # => [-1, 0]`

`ary.drop_while{ |x| x.nonzero? } # => [0, 1]`
`ary.drop_while(:right){ |x| x.nonzero? } # => [0, 1]`
`ary.drop_while(:left){ |x| x.nonzero? } # => [1]`

`ary.take_until{ |x| x.zero? } # => [-1]`
`ary.take_until(:right){ |x| x.zero? } # => [-1]`
`ary.take_until(:left){ |x| x.zero? } # => [-1, 0]`

`ary,drop_until{ |x| x.zero? } # => [0, 1]`
`ary,drop_until(:right){ |x| x.zero? } # => [0, 1]`
`ary.drop_until(:left){ |x| x.zero? } # => [1]`

#take_* gets the left bucket and #drop_* gets the right bucket - the default would be to place the trigger in the right bucket (take_while and drop_while current behave with :right as their option in this scenario).

John

----------------------------------------
Feature #16446: Enumerable#take_*, Enumerable#drop_* counterparts with positive conditions
https://bugs.ruby-lang.org/issues/16446#change-83364

* 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/

^ permalink raw reply	[flat|nested] 8+ messages in thread

* [ruby-core:96442] [Ruby master Feature#16446] Enumerable#take_*, Enumerable#drop_* counterparts with positive conditions
       [not found] <redmine.issue-16446.20191223082605@ruby-lang.org>
                   ` (2 preceding siblings ...)
  2019-12-23 20:29 ` [ruby-core:96440] " wishdev
@ 2019-12-23 21:52 ` sawadatsuyoshi
  2019-12-23 22:12 ` [ruby-core:96443] " wishdev
                   ` (3 subsequent siblings)
  7 siblings, 0 replies; 8+ messages in thread
From: sawadatsuyoshi @ 2019-12-23 21:52 UTC (permalink / raw)
  To: ruby-core

Issue #16446 has been updated by sawa (Tsuyoshi Sawada).


wishdev (John Higgins) wrote:
>I prefer simpler examples
> ary = [-1, 0, 1]

My intention of including two `0`s in the example was to show that there remains an asymmmetry; the search is done from the left, not from the right. In principle, we can still extend the paradigm, but I am not asking for that.

----------------------------------------
Feature #16446: Enumerable#take_*, Enumerable#drop_* counterparts with positive conditions
https://bugs.ruby-lang.org/issues/16446#change-83366

* 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/

^ permalink raw reply	[flat|nested] 8+ messages in thread

* [ruby-core:96443] [Ruby master Feature#16446] Enumerable#take_*, Enumerable#drop_* counterparts with positive conditions
       [not found] <redmine.issue-16446.20191223082605@ruby-lang.org>
                   ` (3 preceding siblings ...)
  2019-12-23 21:52 ` [ruby-core:96442] " sawadatsuyoshi
@ 2019-12-23 22:12 ` wishdev
  2019-12-27 20:43 ` [ruby-core:96526] " daniel
                   ` (2 subsequent siblings)
  7 siblings, 0 replies; 8+ messages in thread
From: wishdev @ 2019-12-23 22:12 UTC (permalink / raw)
  To: ruby-core

Issue #16446 has been updated by wishdev (John Higgins).


sawa (Tsuyoshi Sawada) wrote:
> wishdev (John Higgins) wrote:
> >I prefer simpler examples
> > ary = [-1, 0, 1]
> 
> My intention of including two `0`s in the example was to show that there remains an asymmmetry; the search is done from the left, not from the right. In principle, we can still extend the paradigm, but I am not asking for that.

Nothing wrong with that at all - but I think adding a :reverse options to the methods would accomplish a right to left search feature. 

As I said - declaring what you want to do as opposed to trying to come up with some group of 2 words descriptions seems the wiser path. Especially looking at concepts like reversing the search direction.........

----------------------------------------
Feature #16446: Enumerable#take_*, Enumerable#drop_* counterparts with positive conditions
https://bugs.ruby-lang.org/issues/16446#change-83367

* 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/

^ permalink raw reply	[flat|nested] 8+ messages in thread

* [ruby-core:96526] [Ruby master Feature#16446] Enumerable#take_*, Enumerable#drop_* counterparts with positive conditions
       [not found] <redmine.issue-16446.20191223082605@ruby-lang.org>
                   ` (4 preceding siblings ...)
  2019-12-23 22:12 ` [ruby-core:96443] " wishdev
@ 2019-12-27 20:43 ` daniel
  2019-12-28 17:59 ` [ruby-core:96558] " sawadatsuyoshi
  2020-01-16  6:56 ` [ruby-core:96893] " matz
  7 siblings, 0 replies; 8+ messages in thread
From: daniel @ 2019-12-27 20:43 UTC (permalink / raw)
  To: ruby-core

Issue #16446 has been updated by Dan0042 (Daniel DeLorme).


I think the positive counterparts of `take_while/drop_while` should be `take_until/drop_until`. And although `take_upto` and `drop_upto` are nicely intuitive and descriptive, I can't think of any good names for their negative counterparts (3) and (4). That may be an indication that we'd be better with a parameter as wishdev suggests. However I find that `:left` and `:right` do not read naturally at all. Maybe a number to indicate how many extra items to take or drop:

```ruby
enum.take_while(&:nonzero?) # => [1, 1]
enum.drop_while(&:nonzero?) # => [0, 3, 3, 0, 5, 5]
enum.take_while(+1, &:nonzero?) # => [1, 1, 0]
enum.drop_while(+1, &:nonzero?) # => [3, 3, 0, 5, 5]
```

It's worth noting that there's several other succint ways to accomplish the same thing, so I'm not convinced it's worth adding that many methods. Not everything has to be a one-liner.

```ruby
i = ary.index(&:zero?) || ary.size
ary[0...i]     # => [1, 1]
ary[0..i]      # => [1, 1, 0]
ary[i..-1]     # => [0, 3, 3, 0, 5, 5]
ary[i+1..-1]   # => [3, 3, 0, 5, 5]

n = enum.find_index(&:zero?) || enum.count
enum.take(n)   # => [1, 1]
enum.take(n+1) # => [1, 1, 0]
enum.drop(n)   # => [0, 3, 3, 0, 5, 5]
enum.drop(n+1) # => [3, 3, 0, 5, 5]
```

---

BTW you could also classify the three factors as:

(1) Whether we want to express the items to include (take) or exclude (drop)
(2) Whether we want the left side or the right side in the returned output
(3) Whether we want the items to be inclusive or exclusive of the boundary element

| |(1)|(2)|(3)|method|example|
|--|--|--|--|--|--|
|1|take|left |exclusive|take_before|enum.take_before(&:zero?) # => [1, 1]
|1|take|left |inclusive|take_upto  |enum.take_upto(&:zero?)   # => [1, 1, 0]
|1|take|right|inclusive|take_from  |enum.take_from(&:zero?)   # => [0, 3, 3, 0, 5, 5]
|1|take|right|exclusive|take_after |enum.take_after(&:zero?)  # => [3, 3, 0, 5, 5]
|1|drop|left |exclusive|drop_before|enum.drop_before(&:zero?) # => [0, 3, 3, 0, 5, 5]
|1|drop|left |inclusive|drop_upto  |enum.drop_upto(&:zero?)   # => [3, 3, 0, 5, 5]
|1|drop|right|inclusive|drop_from  |enum.drop_from(&:zero?)   # => [1, 1]
|1|drop|right|exclusive|drop_after |enum.drop_after(&:zero?)  # => [1, 1, 0]

This is what I'd call a declarative style, as it describes *what* the method does, not *how*. `take_while` feels more like an imperative style to me since it describes the *how*, the control flow.

Ultimately there's so many different ways to think about and express any given operation... there's More Than One Way To Do It but they don't all have to be in the ruby core.

----------------------------------------
Feature #16446: Enumerable#take_*, Enumerable#drop_* counterparts with positive conditions
https://bugs.ruby-lang.org/issues/16446#change-83457

* 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/

^ permalink raw reply	[flat|nested] 8+ messages in thread

* [ruby-core:96558] [Ruby master Feature#16446] Enumerable#take_*, Enumerable#drop_* counterparts with positive conditions
       [not found] <redmine.issue-16446.20191223082605@ruby-lang.org>
                   ` (5 preceding siblings ...)
  2019-12-27 20:43 ` [ruby-core:96526] " daniel
@ 2019-12-28 17:59 ` sawadatsuyoshi
  2020-01-16  6:56 ` [ruby-core:96893] " matz
  7 siblings, 0 replies; 8+ messages in thread
From: sawadatsuyoshi @ 2019-12-28 17:59 UTC (permalink / raw)
  To: ruby-core

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/

^ permalink raw reply	[flat|nested] 8+ messages in thread

* [ruby-core:96893] [Ruby master Feature#16446] Enumerable#take_*, Enumerable#drop_* counterparts with positive conditions
       [not found] <redmine.issue-16446.20191223082605@ruby-lang.org>
                   ` (6 preceding siblings ...)
  2019-12-28 17:59 ` [ruby-core:96558] " sawadatsuyoshi
@ 2020-01-16  6:56 ` matz
  7 siblings, 0 replies; 8+ messages in thread
From: matz @ 2020-01-16  6:56 UTC (permalink / raw)
  To: ruby-core

Issue #16446 has been updated by matz (Yukihiro Matsumoto).

Status changed from Open to Rejected

I don't think this many methods only introduce confusion. I feel negative. Besides that, I see the `cut` method is too difficult to use.

Matz.


----------------------------------------
Feature #16446: Enumerable#take_*, Enumerable#drop_* counterparts with positive conditions
https://bugs.ruby-lang.org/issues/16446#change-83907

* Author: sawa (Tsuyoshi Sawada)
* Status: Rejected
* 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/

^ permalink raw reply	[flat|nested] 8+ messages in thread

end of thread, other threads:[~2020-01-16  6:56 UTC | newest]

Thread overview: 8+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
     [not found] <redmine.issue-16446.20191223082605@ruby-lang.org>
2019-12-23  8:26 ` [ruby-core:96419] [Ruby master Feature#16446] Enumerable#take_*, Enumerable#drop_* counterparts with positive conditions sawadatsuyoshi
2019-12-23 15:14 ` [ruby-core:96434] " shevegen
2019-12-23 20:29 ` [ruby-core:96440] " wishdev
2019-12-23 21:52 ` [ruby-core:96442] " sawadatsuyoshi
2019-12-23 22:12 ` [ruby-core:96443] " wishdev
2019-12-27 20:43 ` [ruby-core:96526] " daniel
2019-12-28 17:59 ` [ruby-core:96558] " sawadatsuyoshi
2020-01-16  6:56 ` [ruby-core:96893] " matz

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).