- ベストアンサー
ブロックつきメソッドやyield文の用途について
- ブロックつきメソッドやyield文の用途についての疑問が出てきました。自作メソッドにブロックを引数として与える利点や、処理の流れがわかりやすい場合などについて教えてください。
- ブロックつきメソッドやyield文は、主に大規模なプログラムや複数の人で作業する場合に便利です。しかし、テキストファイルの編集や比較、結合などの目的で100〜200行程度の規模のプログラムを作成する場合には、利点が少ないかもしれません。
- 具体例を交えて、ブロックつきメソッドやyield文の用途について教えてください。どのような場面で活用することができるのか、具体的なイメージを持つことができると助かります。
- みんなの回答 (2)
- 専門家の回答
質問者が選んだベストアンサー
> 「わざわざ、『ブロックを引数として自作メソッドに与える』 > ことの利点ってなに?」 自作メソッドの場合、ブロックを引数として自作メソッドに与えることの利点はない、といって良いかと思います。 そもそも、ブロックを引数を使用することの意味(利点)についてですが、私は以下のように考えています。 ある共通(に使用される)メソッドを外部に提供する場合、そのメソッド呼び出し時、呼び出し側の都合により、 呼び出し側で決めた特殊な処理をそのメソッドで実行してほしい場合に、ブロック引数を使用することにより それが最も低コストで実現可能となる。 例えば、共通なメソッドとしてソートについて考えて見ましょう。 ソートとはある約束に従って、データを並べ替えることです。 ある約束とは、昇順に並べるか、降順に並べるか等の並べ方に関する規則です。 この昇順、降順等の並べ方に関する規則をどのようにして実装するかということですが、 通常、以下の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メソッド) そのブロック引数の記述の仕方を理解しておく必要がある。 となります。
その他の回答 (1)
- play_with_you
- ベストアンサー率37% (112/301)
>自作メソッドの中にブロック処理の内容を書いておくか、 >または、最初から自作メソッドを呼ばずにブロック処理をするほうが、 >処理の流れがわかりやすくてよいのでは??? そう思うならそうやればいいと思いますが、 一応言っておくとブロックつきメソッドは「どういう処理のブロックが渡されるか分からない」ものなので、 「メソッドの中に」なんて書けません。だから、ブロックを渡すほうが「処理の流れがわかりやすくてよい」んです。 大規模とかは無関係です。 [1,2,3].map!{|i| i ** 2} [1,2,3].map!{|i| i.to_s } このどちらも実現できる処理をmap!メソッドの中に分かりやすく書けますか? >ちなみに私は主に テキストファイルの編集や比較、結合..などの目的で >Rubyプログラムを作っていて、規模はせいぜい100~200行くらいです。 確かにそういう限定的なことにしか使わないなら疑問に思うのも仕方ないでしょうね。 必要性が分からないなら無理して使う必要はありません。 自分で必要だと思うときがあったら使えばいいんだし無ければ無いでそれだけの話です。
お礼
play_with_you さん ご回答をありがとうございました。 > 一応言っておくとブロックつきメソッドは「どういう処理のブロックが渡されるか分からない」ものなので、 > .. そもそも、この↑前提条件を理解していなかったためにでた疑問なんですね。 「ブロックの内容がわからない」、 「メソッドに渡すブロックの内容が変更・追加になる可能性がある」 ..というコトを考えると、用途は理解できました。 おっしゃるとおり、 「必要性が分からないなら無理して使う必要はない」 のですが、今は勉強中で、 「『そういうやりかたもある』ということからまずは知っておきたい」 「他の人が書いたコードにブロックつきメソッドが含めれていても理解できるようにしておきたい」 ..という背景があり、質問をしました次第です。 以上です。
お礼
tatsu99 さん たいへん丁寧なご回答をありがとうございました。 よく理解できました。 仕様変更等への対応時の、コストを下げる方法の一つ..ということなんですね。 共通処理の部分(メソッド)と、個別処理(個別指定)の部分(ブロック)との 切り分けが難しそうですが、経験を積めば分けられるようになるんでしょうか..。 将来、もっと多様なプログラムを作る必要が出てきたときのために、 また、他のかたが書いた ブロックつきメソッドがついたコードを理解するためにも、 使い方を腹に落としてこうと思います。 ありがとうございました!