tap の「使え方」

tap に渡すブロックで break をするとその break が tap の返り値を決定する。例えば、true.tap { break false } は false になる。

さて、以下の関数は、数当てゲームのヌメロンでプレーヤーの推測と答えを比較して、イート数(位置も数もあっている)とバイト数(数はあっているが位置が違う)を返すものだ。

def judge ans, try
  eat = ans.zip(try).count{|a,b| a==b}
  [eat, (ans & try).size - eat]
end

judge [1,2,3], [1,3,4] # => [1,1]

ここでこの eat という変数は、式の意味を説明することで、コードを人間にとって理解しやすくしているだけではなくて、計算量の制御のために必要なものだ。

関数を強引に1つの式で書くとイートの式が重複してしまい、同じ計算が2度行われてしまう。

def judge ans, try
  [ans.zip(try).count{|a,b| a==b},
   (ans & try).size - ans.zip(try).count{|a,b| a==b}]
end

これは望ましくない。ところが、あなたは宗教的の理由で代入文を書くことができない。

心配はない。その場合でも tap と break を使えば以下のように書ける。

def judge ans, try
  ans.zip(try).count{|a,b| a==b}.tap do |eat|
    break [eat, (ans & try).size - eat]
  end
end

これであなたのコードは効率的な上に清浄だ。