> 「わざわざ、『ブロックを引数として自作メソッドに与える』
> ことの利点ってなに?」
自作メソッドの場合、ブロックを引数として自作メソッドに与えることの利点はない、といって良いかと思います。
そもそも、ブロックを引数を使用することの意味(利点)についてですが、私は以下のように考えています。
ある共通(に使用される)メソッドを外部に提供する場合、そのメソッド呼び出し時、呼び出し側の都合により、
呼び出し側で決めた特殊な処理をそのメソッドで実行してほしい場合に、ブロック引数を使用することにより
それが最も低コストで実現可能となる。
例えば、共通なメソッドとしてソートについて考えて見ましょう。
ソートとはある約束に従って、データを並べ替えることです。
ある約束とは、昇順に並べるか、降順に並べるか等の並べ方に関する規則です。
この昇順、降順等の並べ方に関する規則をどのようにして実装するかということですが、
通常、以下の2通りの方法があります。
1)並べ方の規則をある引数の具体的な値で指定させる。
例えば、並び順(1:昇順 2:降順)を引数で指定する方法です。
2)並べ方の規則を呼び出し側に記述させる。
これが、ブロックを引数として指定するやり方で、
昇順でソートしたい場合、そのようにブロック内に記述し、
降順でソートしたい場合、そのようにブロック内に記述します。
では、上記のどちらがよい方法かということですが、各々の長所、短所は以下のようになります。
1)の方法について(引数でソート順を指定する方法)
長所:
パラメータの指定がわかり易いので、初心者でも簡単に利用可能。
短所:
ソート時の並べ方が、あらかじめ決まっているので、別の並べ方が必要になった場合は、
ソートメソッド自体も変更が必要になる。
例えば、昇順、降順のほかに以下のような並べ方が必要になったとします。
「奇数を前半にならべ、偶数を後半にならべる。奇数については昇順にならべ、偶数については
降順にならべる」
これを実装するためには、引数に「3:特殊ソート」を追加し、更にソートメソッド自体も
修正する必要があります。
2)の方法について(ブロックを引数で指定する方法)
長所:
上記の特殊ソートのようなソートが必要になった場合でも、ソートメソッドは一切変更の必要はない。
呼び出し側で、ブロック引数内にその規則を記述すれば、実現可能となる。
短所:
ブロック引数の使い方が、初心者にはハードルが高いので、バグを誘発しやすい。
上記を踏まえて、ブロックを引数としてソートを行なう場合、どのくらい自由自在にソートが可能かを以下の
ソースで確認して下さい。
--------------------------------------------
datas = [ 10,13,15,16,5,6,80,81,50,51,100,120]
# そのまま表示
p "そのまま表示"
p datas
# 昇順ソート(ブロック指定無しのソート)
result = datas.sort
p "昇順"
p result
# 降順ソート
result = datas.sort{|x,y| y<=>x}
p "降順"
p result
# 特殊なソート
# 奇数を前半に、偶数を後半に並べる。
# 奇数は昇順に並べる。
# 偶数は降順に並べる。
result = datas.sort{|x,y|
if x%2 == 0
# xが偶数の場合
if y%2 == 0
# yが偶数の場合、降順ソート
y<=>x
else
# yが奇数の場合、yを前半へ
1
end
else
# xが奇数の場合
if y%2 == 0
# yが偶数の場合、yを後半へ
-1
else
# yが奇数の場合、昇順ソート
x<=>y
end
end
}
p "特赦なソート"
p result
# 1人は[名前,性別,年齢]の配列(性別は1:男、2:女)
people = [["taro",1,20],["hanako",2,20],["jiro",1,18],["aiko",2,22],["saburo",1,50],["hanae",2,55]]
# そのまま表示
p "そのまま表示"
p people
# 名前順(ブロック指定無しのソート)
p "名前順"
result = people.sort
p result
# 性別、年齢順
result = people.sort{|x,y|
if x[1] != y[1]
x[1] <=> y[1]
else
x[2] <=> y[2]
end
}
p "性別、年齢順"
p result
--------------------------------------------
実行結果は以下のようになります。
--------------------------------------------
"そのまま表示"
[10, 13, 15, 16, 5, 6, 80, 81, 50, 51, 100, 120]
"昇順"
[5, 6, 10, 13, 15, 16, 50, 51, 80, 81, 100, 120]
"降順"
[120, 100, 81, 80, 51, 50, 16, 15, 13, 10, 6, 5]
"特赦なソート"
[5, 13, 15, 51, 81, 120, 100, 80, 50, 16, 10, 6]
"そのまま表示"
[["taro", 1, 20], ["hanako", 2, 20], ["jiro", 1, 18], ["aiko", 2, 22], ["saburo", 1, 50], ["hanae", 2, 55]]
"名前順"
[["aiko", 2, 22], ["hanae", 2, 55], ["hanako", 2, 20], ["jiro", 1, 18], ["saburo", 1, 50], ["taro", 1, 20]]
"性別、年齢順"
[["jiro", 1, 18], ["taro", 1, 20], ["saburo", 1, 50], ["hanako", 2, 20], ["aiko", 2, 22], ["hanae", 2, 55]]
--------------------------------------------
結論としては、
個人環境で開発を行なう場合は、ブロックを引数とするメソッドは作成する必要はない。
理由は、メソッドに対する要件があらかじめ決まっているので、開発後にもし要件が追加に
なっても、その時点で対応すればよい。
但し、rubyで標準で提供されているメソッドでブロック引数をとるものについては(例えばsortメソッド)
そのブロック引数の記述の仕方を理解しておく必要がある。
となります。
お礼
tatsu99 さん たいへん丁寧なご回答をありがとうございました。 よく理解できました。 仕様変更等への対応時の、コストを下げる方法の一つ..ということなんですね。 共通処理の部分(メソッド)と、個別処理(個別指定)の部分(ブロック)との 切り分けが難しそうですが、経験を積めば分けられるようになるんでしょうか..。 将来、もっと多様なプログラムを作る必要が出てきたときのために、 また、他のかたが書いた ブロックつきメソッドがついたコードを理解するためにも、 使い方を腹に落としてこうと思います。 ありがとうございました!