ブロックを受け取る関数を作ったり使ったりすると便利だよという話をします
ブロック構文は普段からよく使ってるかと思います、例えばArray#eachとか:
[1, 2, 3].each do |item|
puts item
end
逆にブロックを受け取るeachみたいなメソッドは以下のように作れます。引数の定義にProcオブジェクトを使うだけOKです。
def each(&do_something_block)
for index in 0..(self.length)
item = self[index]
do_something_block.call(item)
end
end
なんの役に立つ?
違いとしては、普通の関数とブロックを受け取る関数では、抽出するロジックの階層が異なります
ここでいう階層とは以下のような入れ子構造のことです:
上の階層のロジック {
下の階層のロジック
}
たとえば以下の例だと:
if x > 100
puts x+1
end
上の階層のロジックは:
if x > 100
....
end
下の階層のロジックは:
puts x+1
下の階層を抽出するのが普通の関数
def puts_x_plus_one(x)
puts x+1
end
上の階層を抽出するのがブロックを受け取る関数
def do_something_when_over_100(x, &block)
block.call(x) if x > 100
end
やりたいこと
要件
2つの配列から、ブロックで渡した条件で対応ペアを抽出できるメソッド(#match)を作りました
渡されたブロックがtruthyな値を返すとき、対応ペアとなります:
pairs = ours.match(theirs) do |our_item, their_item|
specified_condition(our_item, their_item)
end
なお、続けていくつも条件を適用したいので、マッチしたアイテムは逐次にpopしていく仕様です
まずは名前で対応付け:
pairs = characters.match(pages) do |chara, page|
chara.name == page.title
end
名前でダメなやつは時期で対応付け:
pairs = characters.match(pages) do |chara, page|
same_month(chara.created_at, page.created_at)
end
それでもダメなやつはidを直接指定して対応付け:
pairs = characters.match(pages) do |chara, page|
chara.id == 50 && page.id == 100
end
何らかの条件でペアを抽出するという汎用的な(ブロックを受け取る)関数を作った結果、どんな対応条件でも後から簡単に試したり追加したりできた
未確定な/変動しうる仕様をうまくカバーできる一般的な問題を見つけて解いた結果
ブロックを受け取る関数を使って、良い感じに一般的な問題を解いておくと、些細な仕様の変更や追加には余裕で対応できるコードが書けていいゾ~これ