【Ruby】ブロック(再)

ブロックとは

 ブロックはメソッドを呼び出すときに記述でき、yield 式を使って任意のタイミング、回数実行することができます。また、for 式, if 式などはスコープが作成されないのに対し、ブロックは新たにスコープを作成します。


ブロックと yield

メソッドの中で yield を呼び出すと、受け取ったブロックを実行します。

def func x
  x + yield
end

func(1){ 2 + 1 } #=> 4

 { } で囲まれている部分がブロックです。このブロックは3を返し、メソッド func では x とブロックの実行結果( yield )を足した 4 が実行結果になっています。


 { } の代わりに do end で記述できます。ブロックが複数行の時に使うことが多いです。

func(1) do
  x = 2
  x += 2
end
#=> 5


 ブロックを渡さない場合は例外が発生します。

func(1) #=> LocalJumpError: no block given (yield)


 block_given? メソッドを用いて、ブロック付きで呼び出された場合にのみ行いたい処理を記述することができます。

def move
  puts 'walk'
  yield if block_given?
  puts 'stop'
end

move               #=> walk stop
move{ puts 'run' } #=> walk run stop



スコープの比較

 for 式、if 式ではスコープが作成されないとのことでした。これは for 式や if 式の中で定義した変数が外から参照できるということになります。

for i in 2..4
  bar = 1
end

puts bar #=> 1
puts i   #=> 4
if true
  hoge = 1
end

puts hoge #=> 1


 これに対しブロックは新たにスコープを作成します。 ブロック内で定義された変数を外から参照することは出来ません。

def hoge
  piyo = 1
end

puts piyo #=> NameError


 ブロックにはもう一つ重大な機能があります。ブロック生成時の変数をブロック内で参照でき、その変数の更新結果が外部に影響される、という機能です。

def foo y
  y + yield
end

x = 2

puts foo(1) { x+=2 } #=> 5
puts x               #=> 4

ブロックの実行により、x の値を取得、更新しています。このときの x はブロック外の変数と同一の変数であり、値ではなく変数そのものが共有されていることになります。


このような対応付けはユーザから直接指定できず変更もできないので、代入ではなく束縛といいます。このように処理の生成時の環境を束縛するものは、一般的にクロージャと呼ばれます。

まとめ

  • { } か do end を使ってメソッドにブロックを渡し、yield で実行
  • スコープが for や if と異なる
  • ブロックはクロージャとしても使用できる



参考書籍

Ruby公式資格教科書 Ruby技術者認定試験 Silver/Gold対応 (EXPERT EXPASS)

Ruby公式資格教科書 Ruby技術者認定試験 Silver/Gold対応 (EXPERT EXPASS)

パーフェクトRuby (PERFECT SERIES 6)

パーフェクトRuby (PERFECT SERIES 6)