[Ruby]Blockを使いこなすとRubyが面白くなるらしい

Rubyを始めたばかりの初心者向けの記事です

Blockの意味がわからないーという人を見かけたので


環境

ruby 2.3.0p0 (2015-12-25 revision 53290) [x86_64-darwin14]

RubyのBlockとは


以下のdo ~ endまたは{ ~ }に囲まれた部分をいう


[1, 2, 3].map do |i|
i ** 2
end
# => [1, 4, 9]

i ** 2i * iと同じ


上記と同じ動きをするコードをあえてwhile文で実装してみる


def my_map(arr)
result = []
i = 0

while i < arr.size
result[i] = arr[i] * arr[i]
i += 1
end
return result
end
p my_map([1, 2, 3]) # => [1, 4, 9]

Blockとはresult[i]に代入するための処理を記述したものと言えます

いわゆる匿名関数と考えてOKです


while文にblockを渡せるようにしてみる


def my_map(arr, &block)
result = []
i = 0

while i < arr.size
result[i] = block.call(arr[i])
i += 1
end
return result
end

result = my_map([1, 2, 3]) do |i|
i ** 2
end
p result # => [1, 4, 9]

Blockを受け取る宣言


まずはこの部分から見ていきます

def my_map(arr, &block)

arr には渡される配列が設定されます (この例だと [1, 2, 3] の配列)

&block は &の部分でブロックを引数として受け取るという宣言になります
(なので &hoge でもなんでも良い)


渡されたBlockを実行するには block.call


渡されたBlockを実行するには block.call(arr[i]) を実行します

callメソッドに引数として 現在の配列の値を渡しています


渡すBlockのコード


my_map([1, 2, 3]) do |i|
i ** 2
end

ここでiにはblock.call(arr[i])arr[i]の値が設定されます


ここまでのまとめ



  • Blockは匿名関数のように外部から処理を渡すための機能

  • Blockを受け取るようにするには&blockと記述する

  • 渡されたブロックを実行するにはblock.callを実行する

  • block.callには引数を渡すことができる


RubyのBlockを使い倒す


以下のコードは渡された引数によって処理するメソッドを変化させているメソッドです
意図としては例外が発生した時に、AlertMailer.send(e)が呼ばれることを保証したい
になります


def execute(type)
case type
when :my_job_1
run_my_job_1
when :my_job_2
run_my_job_2
end
rescue => e
AlertMailer.send(e)
else
NoticeMailer.send()
end

意図としてはわかりますが、このままではrun_my_job_2を実行するときに引数が必要になった
などのケースに対応するのが大変になります

そこでこのコードをBlockを使って書き換えてみます


def execute(&block)
block.call
rescue => e
puts "失敗"
else
puts "成功"
end

def run_my_job_1(foo)
execute do
# something
if foo.nil?
raise "Error"
end
end
end

run_my_job_1(nil) # => 失敗
run_my_job_1(1) # => 成功

このようにBlockを渡せるようにすることで外側の処理を共通化し、内側の処理を個別で記述することができるようになります







Blockについて詳しく知る


Block中の return と next


以下はありがちな記述ミスです
iの値が"1"の時に"100"を返すと書きたかったのでしょう


[1, 2, 3].map do |i|
return 100 if i == 1
return i ** 2
end

正しくはnextを使います


[1, 2, 3].map do |i|
next 100 if i == 0
i ** 2
end
# => [100, 4, 9]

さてでは途中で処理を辞めたい場合はどのようにすればいいのでしょう?


yieldとはなにか


mapをwhileとblockで書きなおした例のコードをyieldで書きなおしてみます


def my_map(arr)
result = []
i = 0

while i < arr.size
result[i] = yield arr[i]
i += 1
end
return result
end

result = my_map([1, 2, 3]) do |i, j|
i ** 2
end
p result

ポイントは以下になります



  • my_map(arr)となり&blockが消えている

  • block.call(arr[i])yield arr[i]になっている


つまり yieldblock.callを省略したものといえます

また、block.callとする場合にはメソッドの引数に&blockと明示する必要があったものがその必要がなくなっています(blockという変数を利用しなくなったため)


ただ、Rubyの技術力にばらつきがあったりする場合は&blockとブロックを受け取ることを明示するようにしたほうが良いでしょう


Blockの実用例


Rubyのライブラリ(Gem)の設定を記述するのによく使われています
以下のようにRailsのinitializerで記述することができるようになっています


Foo.configure do |config|
config.hoge = :foo
end

KaminariというGemでのconfig設定


def self.configure(&block)
yield @config ||= Kaminari::Configuration.new
end

  • @configが存在しない場合は Kaminari::Configuration のインスタンスを生成

  • 渡されてきたBlockに対して生成した@configを渡す
スポンサーサイト
上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。