[ruby-core:96385] [Ruby master Feature#16441] Enumerable#take_while_after

[email protected]
Newsgroups gmane.comp.lang.ruby.core
Message-ID <redmine.issue-16441.20191221132215.1e53415c0b53f8c8@ruby-lang.org>
Issue #16441 has been reported by zverok (Victor Shepelev).

----------------------------------------
Feature #16441: Enumerable#take_while_after
https://bugs.ruby-lang.org/issues/16441

* Author: zverok (Victor Shepelev)
* Status: Open
* Priority: Normal
* Assignee: 
* Target version: 
----------------------------------------
The method is just like `#take_while`, but also includes the item where condition became true.

Examples of usefulness:
```ruby
str = <<DOC
prologue
<<
1
2
3
>>
epilogue
DOC
# Imagine we want to take everything starting from << to >> in short and clean Ruby
# Surprisingly, our best guess would be infamous flip-flop:
p str.each_line(chomp: true).filter_map { _1 if _1 == '<<'.._1 == '>>' }
# => ["<<", "1", "2", "3", ">>"]

# Trying to achieve this with Enumerator, you _almost_ can express it, but...
p str.each_line(chomp: true).drop_while { _1 != '<<' }.take_while { _1 != '>>' }
# => ["<<", "1", "2", "3"] -- the last line is lost.

# So, Enumerable leaves us with this (which is harder to read, due to additional `.first`):
p str.each_line(chomp: true).drop_while { _1 != '<<' }.slice_after { _1 == '>>' }.first
# => ["<<", "1", "2", "3", ">>"]

# With proposed method:
p str.each_line(chomp: true).drop_while { _1 != '<<' }.take_while_after { _1 != '>>' }
# => ["<<", "1", "2", "3", ">>"]
```

The idea is the same as with flip-flops `..` vs `...` (sometimes we need to include the last element matching the condition, sometimes don't), and `while ... end` vs `do ... while`. Another example (from `Enumerator.produce` [proposal](https://bugs.ruby-lang.org/issues/14781)):

```ruby
require 'strscan'
scanner = StringScanner.new('7+38/6')
p Enumerator.produce { scanner.scan(%r{\d+|[-+*/]}) }.take_while { !scanner.eos? } # expresses meaning, but loses last element
# => ["7", "+", "38", "/"]
p Enumerator.generate { scanner.scan(%r{\d+|[-+*/]}) }.slice_after { scanner.eos? }.first # slice_after {}.first again
# => ["7", "+", "38", "/", "6"]
p Enumerator.produce { scanner.scan(%r{\d+|[-+*/]}) }.take_while_after { !scanner.eos? }
# => ["7", "+", "38", "/", "6"]
```

PS: Not sure about the name, suggestions are welcome



-- 
https://bugs.ruby-lang.org/

Unsubscribe: <mailto:[email protected]?subject=unsubscribe>
<http://lists.ruby-lang.org/cgi-bin/mailman/options/ruby-core>
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.