タイトルでネタバレしつつある気がしますが、唐突に問題を出してみます。
data = {'hoge' => false}
p data.any? do |key, val|
val
end
この結果は何が出力されるでしょう?
any?
といえば、レシーバの要素が全てfalseyのときだけfalse、1つでもtruthyの要素があればtrueを返すEnumerableのメソッドです。
false、と答えた人はたぶん
「valがfalseなんだから、そりゃfalseだろ」って認識だと思います。
trueと答えた人は
「そんな聞き方するってことはtrueだろ」って人と
ちゃんと理由が分かる人の二種類がいると思います。
後輩に訊ねたところ、前者の答えが返ってきていささか面白くなくてぷんすこです。
結果は当然流れ的にtrue。
はて、ここで何が起きているのか?
ここで掲題の話になるわけです。
公式ドキュメントにはこうあります。
{ ... } の方が do ... end ブロックよりも強く結合します
Ruby 2.1.0 リファレンスマニュアル
つまり
data = {'hoge' => false}
p(data.any?) do |key, val|
val
end
# => true
こうなっちゃってたわけですね。
data.any?
にはブロックを渡しておらず、data
の要素が全部偽であることのチェックをした結果を出力していたわけです。
またdata
はHashなので、空Hashでもない限りは要素を持っている=trueとなりますので、こういった結果になっていたわけです。
ここでdo ... end
ではなく{ ... }
を使用している場合は想定通りの結果になります。
data = {'hoge' => false}
p data.any? { |key, val|
val
}
# => false
また、p
がブロックを受けてゴニョゴニョするようなメソッドだったらまた違った結果になっていたことでしょう。
do ... end
と{ ... }
の結びつきの違いの話は非常に有名ですが、初心者ですと違いが分からずに同じようにハマるかもしれないのでここに共有しておきます。
私の場合も独自のコーディングルールで
ワンライナー(1行)で記述する際は { ... }を使い、
ワンライナーで可読性が落ちる場合には do ... end で複数行で記述しましょう。
というものがありまして、それを守りつつany?
の挙動を確認しているときに引っかかってしまいました。