Ruby学習メモ ブロック

<ブロック>

・ブロックはメソッドの引数として渡すことができる処理のかたまりである。ブロック内で記述した処理は必要に応じてメソッドから呼び出される。

 

numbers = [1, 2, 3 4]

sum = 0

 

numbers.each do |n|

  sum += n

end

 

sum #=> 10

 

eachメソッドの役割は配列の要素を最初から最後まで順番に取り出すこと。しかし、取り出した要素をどう扱うのかは、その時の要件で変化する。

配列の要素を順番に取り出す作業はeachメソッドで行い、その要素をどう扱うかはブロックに記述する。上のコードでいうとdoからendとなる。

 

|n|のnはブロック引数と呼ばれ、eachメソッドから渡された配列の要素が入る。上のコードでは、nには1, 2, 3, 4が順番に渡される。

ブロックの内部では、sum += nように自由にRubyのコードを書くことができ、変数sumに配列の各要素nを加算するコードが書かれている。

 

 

<配列の要素を削除する条件を自由に指定する>

・delete_ifメソッドを使って、配列から奇数だけを削除するコードの例

 

a = [1, 2, 3, 1, 2, 3]

a.delete_if do |n|

  n.odd?

end

a #=> [2, 2]

 

delete_ifメソッドもeachメソッドと同じように、配列の要素を順番に取り出し、そしてその要素をブロックに渡す。

delete_ifメソッドはブロックの戻り値をチェックする。その戻り値が真であれば、ブロックに渡した要素配列から削除し、偽であれば配列に残したままにする。

ブロックの戻り値はメソッドと同様、最後に評価された式となる。

delete_ifメソッドは、「配列の要素を順番に取り出すこと」と「ブロックの戻り値が真であれば要素を削除すること」という共通処理を提供している。

「要件を問わず共通する処理」はメソッド自身に、「要件によって異なる処理」はブロックにそれぞれ分担させる。

 

 

<ブロック引数とブロック内の変数>

ブロック引数のnはnでなくてもよく、引数の名前は自由に決めれる。

 

numbers = [1, 2, 3 4]

sum = 0

 

numbers.each do |n|

  sum += n

end

 

# ブロック引数の名前は何でも良い。

numbers.each do |element|

  sum += element

end

sum #=> 10

 

 

・偶数のみ、値を10倍にしてから加算するコードの例。

 

numbers = [1, 2, 3, 4]

sum = 0

 

numbers.each do |n|

  sum_value = n.even? ? n * 10 : n

  sum += sum_value

end

 

sum #=> 64

 

 

sum_valueはブロック内で初めて登場した変数であり、このような変数のスコープ(有効範囲)はブロック内部のみとなる。ブロックの外でsum_valueを参照するとエラーが発生する。

sumはブロックの外で作成されたので、ブロックの内部でも参照可能である。

 

 

・ブロック引数の名前をブロックの外にある変数の名前と同じにすると、ブロック内ではブロック引数の値が優先して参照される。(名前の重複により、他の変数やメソッドが参照できなくなることをシャドーイングという。)

numbers = [1, 2, 3, 4]

sum = 0

sum_value = 100

 

numbers.each do |sum_value|

  sum += sum_value

end

 

sum #=> 10

 

 

<do…endと{ }>

Rubyの文法上、改行を入れなくてもブロックは作動する。

 

numbers = [1, 2, 3, 4]

sum = 0

 

numbers.each do |n| sum += n end

 

sum #=> 10

 

 

・do…endを使用する代わりに、{ }で囲んでもブロックを作成できる。

numbers = [1, 2, 3, 4]

sum = 0

 

numbers.each { |n| sum+= n }

 

sum #=> 10

 

 

・do…endと{ }はどちらも同じブロックなので、{ }を使い、ブロックの内部を開業させることも可能である。

numbers = [1, 2, 3, 4]

sum = 0

 

numbers.each { |n|

  sum += n

}

 

sum #=> 10

 

・改行を含む長いブロックを書く場合はdo…end

・一行でコンパクトに描きたいときは{ }

上記のように使い分けられるケースが多い。