Rubyの配列(Array)にはany?
という便利メソッドがあります。
すべての要素が偽である場合に false を返します。真である要素があれば、ただちに true を返します。
ブロックを伴う場合は、各要素に対してブロックを評価し、すべての結果が偽である場合に false を返します。ブロックが真を返した時点で、ただちに true を返します。
上記はドキュメントから転記しました。
any?を使うことで、配列内に特定の条件を満たした要素が1つ以上あること(または存在しないこと)を確認することができます。
また、真の要素が見つかったら以降の判定は行われないと記載されているため計算効率も良いです。
簡単に動作確認をしてみます。
配列[5, 3, 4]
の中に3以下の数値があるかをチェックしています。
何が判定されたか可視化できるように判定ごとにp _1
で判定した要素を出力するようにしています。
irb(main):005:0> ary = [5, 3, 4]
irb(main):012:1> ary.any? do
irb(main):013:1* p _1
irb(main):014:1* _1 <= 3
irb(main):015:0> end
5
3
=> true
2つ目の要素3がtrueとなるため、以降の要素4は判定されていないことがわかります。
また、配列内の要素の真偽を判定するだけであれば、ブロックやパタメーターなしで使うことができます。
irb(main):028:0> [nil, 'a'].any?
=> true
any? を || の代わりに使わない
ここからがこの記事の本題です。
ブロックやパラメーターなしのany?を応用すると、複数項目のor条件の代わりに使えることがわかります。
例えばifの条件であったり、?メソッドの戻り値などでany?を使えます。
if hoge? || fuga? || piyo?
# hoge?, fuga?, piyo?のいずれかがtrueの場合に実行される
end
# any?を使って下記のように書くこともできる
if [hoge?, fuga?, piyo?].any?
end
# hoge?, fuga?, piyo?のいずれかがtrueか?
def any_truthy?
hoge? || fuga? || piyo?
end
# any?を使って下記のように書くこともできる
def any_truthy?
[hoge?, fuga?, piyo?].any?
end
当初、私はどちらの書き方も好みの問題だけで、どちらでも良いと考えていました。
ただ、このパターンの場合は明確に||の方が良いということができます。
実際にhoge?, fuga?, piyp?メソッドを使って処理を確認してみます。
今回はfuga?だけがtrueを返却するようにしています。また、呼び出されたことがわかるようにプリントするようにしています。
def hoge?
p 'hoge'
false
end
def fuga?
p 'fuga'
true
end
def piyo?
p 'piyo'
false
end
ここで下記のように判定した場合、どうなるでしょうか?
[hoge?, fuga?, piyo?].any?
前から処理していくと、fuga?がtrueを返却するのでpiyo?は確認しなくてもany?はtrueが確定しているのですがpiyo?も実行されてしまいます。
irb(main):015:0> [hoge?, fuga?, piyo?].any?
"hoge"
"fuga"
"piyo"
=> true
一方、||を使うとpiyo?の判定は行われません。
irb(main):015:0> hoge? || fuga? || piyo?
"hoge"
"fuga"
=> true
上記の通り、any?を使った場合は3つのメソッドが全て呼び出されますが、||の場合はhoge?やfuga?がtrueになった場合に後続メソッドが呼び出されないので効率が良いです。
any?も真の要素が見つかれば処理が中断するのではなかったっけ??という疑問が起きると思いますが、[hoge?, fuga?, piyo?].any?
の処理を分解すると下記のように動作しています。
- 最初に配列
[hoge?, fuga?, piyo?]
を生成- この時点でhoge? / fuga? / piyo?は判定されて、
[false, true, false]
という配列が生成されています。
- この時点でhoge? / fuga? / piyo?は判定されて、
-
[false, true, false]
を先頭から順に判定- 2つ目がtrueのため判定は終了します。最後のfalseの判定は行われません。
判定の前に配列の生成を行ってしまうため、配列内の要素はすべて実行されてしまうんですね。
上記のように||の代わりにany?を使う場合は、好みの問題だけでなくパフォーマンスにも影響があるので||を使うようにしましょう。