Project

General

Profile

Feature #16441

Enumerable#take_while_after

Added by zverok (Victor Shepelev) 7 months ago. Updated 6 months ago.

Status:
Rejected
Priority:
Normal
Assignee:
-
Target version:
-
[ruby-core:96385]

Description

The method is just like #take_while, but also includes the item where condition became true.

Examples of usefulness:

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:

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 the last line is lost:

str.each_line(chomp: true).drop_while { _1 != '<<' }.take_while { _1 != '>>' }
# => ["<<", "1", "2", "3"]

So, Enumerable leaves us with this (which is harder to read, due to additional .first):

str.each_line(chomp: true).drop_while { _1 != '<<' }.slice_after { _1 == '>>' }.first
# => ["<<", "1", "2", "3", ">>"]

With proposed method:

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):

require 'strscan'
scanner = StringScanner.new('7+38/6')

Enumerator.produce { scanner.scan(%r{\d+|[-+*/]}) }.take_while { !scanner.eos? }
# => ["7", "+", "38", "/"]

Enumerator.generate { scanner.scan(%r{\d+|[-+*/]}) }.slice_after { scanner.eos? }.first
# => ["7", "+", "38", "/", "6"]

Enumerator.produce { scanner.scan(%r{\d+|[-+*/]}) }.take_while_after { !scanner.eos? }
# => ["7", "+", "38", "/", "6"]

PS: Not sure about the name, suggestions are welcome


Related issues

Related to Ruby master - Feature #16446: Enumerable#take_*, Enumerable#drop_* counterparts with positive conditionsRejectedActions

Updated by nobu (Nobuyoshi Nakada) 7 months ago

Flip-flop is the winner!!!

Updated by shevegen (Robert A. Heiler) 7 months ago

This is of course only my personal opinion, but I believe that long
names can be somewhat problematic. Now I myself use really very
long method names, but for ruby as a "basic building block" (that
is the core parts), I think the shorter the method name, the better
(usually that is).

So we have methods such as:

.size
.keys
.uniq

Short and expressive.

And we have some methods with two words:

.each_pair
.take_while

And so forth.

I believe that the net benefit of methods becomes lesser the more words
have to be used for a particular method call - which I mean in general,
primarily, not solely confined to the name/issue here. Like, three
words, or four words, or five words ... or things such as
HashWithIndirectAccess.

I think three words are quite unwieldy. It also feels a bit strange since
this is almost as if you could do a method-chain, like:

.take_while_after
.take.while.after

Reminds me a bit of rspec.

Of course it depends a lot on how someone uses ruby, which "style" is to
be preferred, but for me personally, I much prefer the shorter, simpler
variant whenever that would be possible. It is a bit comparable to
"yield_self" versus "then" - if the question is solely between these two
names, then the name "then" is IMO better, because it is easier to use.

I don't have a good alternative name either, but I am also not sure if there
can be a much simpler name IF the thought behind the suggestion is to
combine so many different method calls in one go.

Perhaps we could have two variants of ruby, one for the simple minds, and
one for the uber gurus that may find haskell too easy. ;-)

The idea is the same as with flip-flops .. vs ...

Well, the difference here is a single (!) character.

I think you are a bit afar from that difference. I also already find
.take_while a peculiar name ...

By the way, another small point to note is that in the method chain,
"while" appears twice. That seems a bit odd to me too.

#3

Updated by sawa (Tsuyoshi Sawada) 7 months ago

  • Description updated (diff)

Updated by Dan0042 (Daniel DeLorme) 7 months ago

take_while_after is rather unwieldy, so in terms of naming maybe I can suggest:

str.each_line(chomp: true).take_from{ _1 == '<<' }.take_upto{ _1 == '>>' }

Updated by sawa (Tsuyoshi Sawada) 7 months ago

Dan0042 (Daniel DeLorme) wrote:

take_while_after is rather unwieldy, so in terms of naming maybe I can suggest:

str.each_line(chomp: true).take_from{ _1 == '<<' }.take_upto{ _1 == '>>' }

Such methods are the same as the cases 6 and 7 in my proposal #16446.

Updated by akr (Akira Tanaka) 6 months ago

mame (Yusuke Endoh) wrote:

akr (Akira Tanaka) What do you think?

I'm neutral with this feature.
I don't like the name, take_while_after, though.

I feel that it's better to consider the method name in wider view, like [Feature #16446].

#8

Updated by akr (Akira Tanaka) 6 months ago

  • Related to Feature #16446: Enumerable#take_*, Enumerable#drop_* counterparts with positive conditions added

Updated by matz (Yukihiro Matsumoto) 6 months ago

  • Status changed from Open to Rejected

I don't see the real-world usage of take_while_after. Use take_while with proper condition.

Matz.

Also available in: Atom PDF