From: Austin Ziegler Date: 2020-12-09T21:35:41-05:00 Subject: [ruby-core:101351] Re: [Ruby master Feature#17333] Enumerable#many? --===============1433737446== Content-Type: multipart/alternative; boundary="000000000000246bf705b6130945" --000000000000246bf705b6130945 Content-Type: text/plain; charset="UTF-8" Content-Transfer-Encoding: quoted-printable I started with the opinion that this wasn=E2=80=99t necessary, but the simp= lest Ruby implementation I could come up with is this: def many? reduce(false) { |f, v| vp =3D block_given? ? yield v : v break true if f && vp vp } end It=E2=80=99s probably a little less efficient than the ActiveSupport extens= ion (which uses two different branches for `block_given?` or not). Something similar was recently suggested to the Elixir core list, and what was decided there is I think a little more generalizable, `count_until` ( https://github.com/elixir-lang/elixir/pull/10532). Here=E2=80=99s a Ruby implementation of what could be Enumerable#count_unti= l? def count_until(limit, match =3D nil, &block) cnt =3D 0 if match warn 'warning: given block not used' if block block =3D ->(v) { v =3D=3D match } elsif !block return [limit, count].min if respond_to?(:size) block =3D ->(_) { true } end stop_at =3D limit - 1 reduce(0) { |c, v| c +=3D 1 if block.(v) break limit if c =3D=3D stop_at c } end # (1..20).count_until(5) # =3D> 5 # (1..20).count_until(50) # =3D> 20 # (1..10).count_until(10) =3D=3D 10 # =3D> true # At least 10 # (1..11).count_until(10) =3D=3D 10 # =3D> true # At least 10 # (1..11).count_until(10 + 1) > 10 # =3D> true # More than 10 # (1..5).count_until(10) < 10 # =3D> true # Less than 10 # (1..10).count_until(10 + 1) =3D=3D 10 # =3D> true # Exactly ten -a On Wed, Dec 9, 2020 at 8:56 AM wrote: > Issue #17333 has been updated by okuramasafumi (Masafumi OKURA). > > > Here are a usecase where we could use `many?` over `count` for better > performance. > > > https://github.com/Homebrew/brew/blob/master/Library/Homebrew/cask/audit.= rb#L188 > > This code, `cask.artifacts.count { |k| k.is_a?(Artifact::Uninstall) } > 1= ` > calls block as many times as the size of `cask.artifact`. In contrast, > `cask.artifacts.many? { |k| k.is_a?(Artifact::Uninstall) }` calls blocks > only before finding second matching artifact, which could improve > performance. > Like this case where we cannot expect the size of collection, using > `count` could cost a lot and introducing `many?` could help. > > ---------------------------------------- > Feature #17333: Enumerable#many? > https://bugs.ruby-lang.org/issues/17333#change-89042 > > * Author: okuramasafumi (Masafumi OKURA) > * Status: Open > * Priority: Normal > ---------------------------------------- > `Enumerable#many?` method is implemented in ActiveSupport. > https://api.rubyonrails.org/classes/Enumerable.html#method-i-many-3F > However, it's slightly different from Ruby's core methods such as `one?` > or `all?`, where they take pattern argument. > I believe these methods should behave the same so that it's easier to > guess and learn. > > We already have `none?`, `one?`, `any?` and `all?`, which translate into > `=3D=3D 0`, `=3D=3D 1`, `> 0` and `=3D=3D self.size`. > `many?` method translates into `> 1`, which is reasonable to exist. > Currently we need to write something this: > > ```ruby > [1, 2, 3].count(&:odd?) > 1 > ``` > > With `many?`, we can make it simpler: > > ```ruby > [1, 2, 3].many?(&:odd?) > ``` > > Pull Request on GitHub is available at > https://github.com/ruby/ruby/pull/3785 > > > > -- > https://bugs.ruby-lang.org/ > > Unsubscribe: > > --=20 Austin Ziegler =E2=80=A2 halostatue@gmail.com =E2=80=A2 austin@halostatue.c= a http://www.halostatue.ca/ =E2=80=A2 http://twitter.com/halostatue --000000000000246bf705b6130945 Content-Type: text/html; charset="UTF-8" Content-Transfer-Encoding: quoted-printable
I start= ed with the opinion that this wasn=E2=80=99t necessary, but the simplest Ru= by implementation I could come up with is this:

=C2= =A0 def many?
=C2=A0 =C2=A0 reduce(false) { |f, v|
=C2= =A0 =C2=A0 =C2=A0 vp =3D block_given? ? yield v : v
=C2=A0 =C2=A0= =C2=A0 break true if f && vp
=C2=A0 =C2=A0 =C2=A0 vp
=C2=A0 =C2=A0 }
=C2=A0 end

= It=E2=80=99s probably a little less efficient than the ActiveSupport extens= ion (which uses two different branches for `block_given?` or not). Somethin= g similar was recently suggested to the Elixir core list, and what was deci= ded there is I think a little more generalizable, `count_until` (https://github.com/elixi= r-lang/elixir/pull/10532).

Here=E2=80=99s a Ru= by implementation of what could be Enumerable#count_until?

=C2=A0 def count_until(limit, match =3D nil, &block)
=C2=A0 =C2=A0 cnt =3D 0

=C2=A0 =C2=A0 if mat= ch
=C2=A0 =C2=A0 =C2=A0 warn 'warning: given block not used&#= 39; if block
=C2=A0 =C2=A0 =C2=A0 block =3D ->(v) { v =3D=3D m= atch }
=C2=A0 =C2=A0 elsif !block
=C2=A0 =C2=A0 =C2=A0 = return [limit, count].min if respond_to?(:size)
=C2=A0 =C2=A0 =C2= =A0 block =3D ->(_) { true }
=C2=A0 =C2=A0 end

<= /div>
=C2=A0 =C2=A0 stop_at =3D limit - 1

=C2= =A0 =C2=A0 reduce(0) { |c, v|
=C2=A0 =C2=A0 =C2=A0 c +=3D 1 if bl= ock.(v)

=C2=A0 =C2=A0 =C2=A0 break limit if c =3D= =3D stop_at

=C2=A0 =C2=A0 =C2=A0 c
=C2= =A0 =C2=A0 }
=C2=A0 end

=C2=A0 # (1..20)= .count_until(5) # =3D> 5
=C2=A0 # (1..20).count_until(50) # = =3D> 20
=C2=A0 # (1..10).count_until(10) =3D=3D 10 # =3D> t= rue # At least 10
=C2=A0 # (1..11).count_until(10) =3D=3D 10 # = =3D> true # At least 10
=C2=A0 # (1..11).count_until(10 + 1) &= gt; 10 # =3D> true # More than 10
=C2=A0 # (1..5).count_until(= 10) < 10 # =3D> true # Less than 10
=C2=A0 # (1..10).count_= until(10 + 1) =3D=3D 10 # =3D> true # Exactly ten

-a

On Wed, Dec 9, 2020 at 8:56 AM <masafumi.o1988@gmail.com> wro= te:
Issue #17333 has been updated by okuramasafum= i (Masafumi OKURA).


Here are a usecase where we could use `many?` over `count` for better perfo= rmance.

https://github.com/H= omebrew/brew/blob/master/Library/Homebrew/cask/audit.rb#L188

This code, `cask.artifacts.count { |k| k.is_a?(Artifact::Uninstall) } > = 1` calls block as many times as the size of `cask.artifact`. In contrast, `= cask.artifacts.many? { |k| k.is_a?(Artifact::Uninstall) }` calls blocks onl= y before finding second matching artifact, which could improve performance.=
Like this case where we cannot expect the size of collection, using `count`= could cost a lot and introducing `many?` could help.

----------------------------------------
Feature #17333: Enumerable#many?
https://bugs.ruby-lang.org/issues/17333#change-8= 9042

* Author: okuramasafumi (Masafumi OKURA)
* Status: Open
* Priority: Normal
----------------------------------------
`Enumerable#many?` method is implemented in ActiveSupport.
https://api.rubyonrails.org/clas= ses/Enumerable.html#method-i-many-3F
However, it's slightly different from Ruby's core methods such as `= one?` or `all?`, where they take pattern argument.
I believe these methods should behave the same so that it's easier to g= uess and learn.

We already have `none?`, `one?`, `any?` and `all?`, which translate into `= =3D=3D 0`, `=3D=3D 1`, `> 0` and `=3D=3D self.size`.
`many?` method translates into `> 1`, which is reasonable to exist.
Currently we need to write something this:

```ruby
[1, 2, 3].count(&:odd?) > 1
```

With `many?`, we can make it simpler:

```ruby
[1, 2, 3].many?(&:odd?)
```

Pull Request on GitHub is available at https://github.com/ruby= /ruby/pull/3785



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

Unsubscribe: <mailto:ruby-core-request@ruby-lang.org?subject=3Dunsubscribe= >
<http://lists.ruby-lang.org/cgi-bin/m= ailman/options/ruby-core>


--
--000000000000246bf705b6130945-- --===============1433737446== Content-Type: text/plain; charset="us-ascii" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Content-Disposition: inline Unsubscribe: --===============1433737446==--