ブロック付きメソッドに対して考える前に、まずブロックとメソッドについて考えてみよう!
##ブロック
do ... end または { ... } の形をしたもの。
初めにdo ... endの記述で書いてみよう!
%w(chida hayashi shimohira).each do |name|
puts name
end
#以下のように表示される
#"chida"
#"hayashi"
#"shimohira"
同様に、{ ... }のパターンでも書いてみよう!
%w(chida hayashi shimohira).each {|name|
puts name
}
#上とおなじ結果になります。
ブロックを多くの方は、繰り返し処理などの時に使用すると思っているのではないでしょうか。
ブロックとは一体なんですか?と質問される時に、多くの方は上の配列を繰り返し処理する時に使う物と答えるのではないでしょうか。
実はブロックは繰り返しのために使用されるというわけでなく、様々な用途で使用されます。
File.open 'README.md' do |file|
puts file.read
end
ここで行なわれている処理は
ファイルを開く ファイルの内容を読み込んで出力する ファイルを閉じるです。
File.openはブロックを実行した後で自動的にファイルを閉じるため、ファイルを閉じ忘れる心配はありません。
このように配列以外でもブロックを使用しています。
またファイルの他にもデータベースのトランザクションなど処理を抽象化する用途に使用されます。
##メソッド
メソッドとはなんでしょうか?一度プログラミングを実行した方であれば既にお気付きの方もいると思いますが、もう上記の例でメソッドは出現しています。
レシーバー.メソッド名
という記述方法でメソッドは記述されます。
%w(chida hayashi shimohira).each do |name|
puts name
end
この上記の例と見ると、%w(chida hayashi shimohira)がレシーバーに当たります。
そしてeachがメソッド名となります。
ここのeachというものは、配列の要素数と同じ回数だけ処理を実行する事が出来ます。
ここまででブロックとメソッドに関して大丈夫でしょうか?大丈夫ですね!😊
それでは、ブロック付きメソッドについて考えていきましょう。
#ブロック付きメソッド
##引数なしのブロック付きメソッド
def block_method
yield "a"
yield "b"
yield "c"
end
block_method do |x|
puts x
end
##引数ありのブロック付きメソッド
def plus(x, y)
yield x, y
end
plus(1, 1) do |a, b|
puts a+b
end
あっ、先ほど上で使用したブロックだ!!と気付きましたでしょうか。ここでもブロックを使用しています。
ここでyieldという構文が出てきました。ブロックを学習するとよく出てくるyieldについて学びましょう。
##yield
yieldは指定した値をブロックのパラメーターに渡してブロック内部を実行します。下のコードを見て学習しましょう。
def block_method
yield "a"
yield "b"
yield "c"
end
block_method do |x|
puts x
end
こちらのyield "a"など指定した"a","b","c"が、|x|(ブロックのパラメーター)に渡して、putsの処理を行います。
ここまでの説明をみて、なんとなく分かったけどいまいち腑に落ちない方、よくわかります。私もそうでした!
ここはある程度プログラミングを学習された方が読み進んでもらえると良いと思います。
実はブロック付きメソッドとyieldは、コールバック関数を簡単に書けるようにしたものなのです。
def block_method(func)
func.call "a"
func.call "b"
func.call "c"
end
block_method( lambda{ |x| puts x } )
これがブロックを省略しないで書いたものです。ここでは深く言及しませんが、このfuncというものは無名関数lambdaという関数ごと引数に渡しているのです。yieldと記述するだけで、これと全く同義のコードを書く事が出来ます。
では最後に問題を解いてみましょう。
2個の配列とブロックを受け取り、配列の各要素ごとにブロックで指定された演算を行うメソッドplusを作れ。例えば、 plus([1,2], [3,4]){|x,y| x*y} は [4,6] を返す。ただし、引数が配列でない場合や、要素の個数が一致しない場合は考えなくてよい。
解答例1
def plus(array1, array2)
array = []
array1.count.times do |n|
array << yield(array1[n], array2[n])
end
p array
end
plus([1, 2], [3, 4]){ |x,y| x+y }
yieldが引数をブロックのパラメーターに渡してブロック内部を実行しています。よって[4,6]という結果が返ってきます。
解答例2
def plus(array1, array2)
array1.map.with_index{ |n, i| yield(n, array2[i]) }
end
p plus([1, 2], [3, 4]){ |x,y| x+y }
#まとめ
ブロックとはdo ... end または { ... } の形をしたもの。
yieldとは指定した値をブロックのパラメータに渡してブロック内部を実行するもの。
##参照
パーフェクトRuby