プログラミング漫遊記

思ったことや、勉強したことをつらつらと。

【Ruby】Enumerableモジュールのメソッドツアー - all?メソッド

こんにちは。はるぐちです。

Enumerableモジュールってご存知でしょうか。そうArrayとかHashとかにインクルードされている便利なやつ。

これのおかげで反復処理がスムーズにできると言っても過言ではないEnumerableモジュールですが、メソッドを全部知っているわけではないなーと思って1記事1メソッドでツアーをすることにしました。

ちゃんとした仕様の説明はRubyリファレンスマニュアル(以下るりま)をみてください。僕は雑に紹介していきます。

all?メソッド

module Enumerable (Ruby 3.1 リファレンスマニュアル)

all?メソッドは各要素を調べて、真偽値を返すメソッドです。

すべての要素が真である場合はtrue、1つでも偽である場合はfalseを返します。

言葉だけではイメージがつかないこともあると思うので、早速irbで遊んでみます。

irbで挙動の確認

Ruby3.1で確認しています。

引数なしの場合

引数なしの場合は各要素を真偽値に変換します。 Rubyの場合はnilfalseの場合はfalseになり、それ以外はtrueに変換されます。

[true, false, true].all?
=> false
[0, 'foo', nil].all?
=> false

基本的にrangeオブジェクトやhashオブジェクトに対して引数なしのall?メソッドを呼び出すのはあまり意味がなさそうです。

# なんでもtrueになる
(1..3).all? # 1も2も3もtrueなので返り値はtrue
=> true

{a: 1, b: nil}.all?
=> true

余談ですが、ハッシュのvalueに対してすべての値がtrueか確認したい時はHash#valuesメソッドを使って配列に変換するのが良さそうだと思いました。

# 配列に変換 valueだけ取り出す
{a: 1, b: false}.values
=> [1, false]


{a: 1, b: false}.values.all?
=> false

引数ありの場合

引数あり(ブロックなし)の場合は引数に対して===メソッドを呼び出し各要素と比較します。 ===メソッドの説明は大変なので割愛したい。(挙動がたくさんあって説明しきれないので、、、😅) class Object (Ruby 3.1 リファレンスマニュアル)

[1, 2, 3].all?(Integer)
=> true
[1.2, 2, 3].all?(Integer)
=> true

['foo', 'bar', 'baz'].all?(/\w+/)
=> true
['foo', 'bar', ''].all?(/\w+/)
=> false

これだけだとイメージしにくい方は以下にイメージ図を参考にしてください。

# イメージ図
[1.2, 2, 3].all?(Integer) の場合

# 各要素に対して順番に 引数 === 要素 を実行していく
Integer === 1.2  false
Integer === 2 true
Integer === 3 true

[false, true, true].all?
=> false

実際には1つでもfalseな要素があった場合はそれ以降の要素は確認されずにfalseが返されるみたいなのでこれはあくまでもイメージと思ってください。

ブロックありの場合

もう少し詳細に条件を絞り込みたい場合はブロック付きでメソッドを呼び出します。

ブロックパラメータは各要素で、ブロックの中でbooleanを返すような条件式を記述します。

# すべて3以上かどうかを調べる
(2..5).all? { |v| v >= 3}
=> false

# すべての要素が偶数かどうか調べる
[2, 4, 6, 8].all(&:even?)


# 全員成人かどうか調べる
users = [
  {name: 'taro', age: 40},
  {name: 'jiro', age: 30},
  {name: 'saburo', age: 20}
]

users.all? { |hash| hash[:age] >= 20 }
=> true

ブロックを用いる場合はHashでも使い道がありそうです。

{a: 1, b: 2}.all? { |k, v| Integer === v }
=> true
{a: 1, b: 2.0}.all? { |k, v| Integer === v }
=> false

まぁ、この例だったら配列に変換したほうがよさそうではありますね😅

引数とブロックを与えた場合

引数とブロックを与えた場合はどちらが優先されるのでしょうか?るりまには記述が見当たらなかったので自分で調べていきます。

[1, 2, 3].all?(Integer) { |v| v >= 2 }
warning: given block not used
true

ご覧の通り引数が優先されるみたいです。 warningが出てるので、ひょっとしたらall?メソッドの話ではなく引数とブロックが両立しないブロック付きメソッドの共通の仕様なのか?(未検証)

まとめ

雑にまとめます。

  • all?メソッドは各要素がすべて真ならtrue、そうでないならfalseを返すメソッド
  • 引数ありの場合は引数 === 要素で検証
  • ブロック付きの場合
    • ブロックパラメータ: 各要素
    • ブロックの中にはbooleanが返るような式を記述