どなブロ

エンジニアァのブログです

たのしいRuby 第11章

ダイエットの調子がいいです

ブロック

  • ブロック…メソッド呼び出しの際に引数と一緒に渡すことのできる処理の塊
オブジェクト.メソッド名(引数リスト) do |ブロック変数|
  繰り返す処理
end

※ 「do~end」を「{~}」とも書ける

ブロックの使われ方

繰り返し

  • ブロック付き呼び出しはよく繰り返しに用いられる
  • オブジェクトに配列がある場合、ひとつずつ取り出しブロック変数に入れる形
  • ハッシュの場合は「[キー, 値]」のペアの配列にして取り出す →つまり、値を取り出すならpair[1]のような形
sum = 0
outcome = {"参加費"=>1000, "ストラップ代"=>1000, "懇親会参加費"=>4000}
outcome.each do |pair|
  sum += pair[1]
end
puts "合計 : #{sum}"

→また、ブロック変数を複数用意すればキーと値をそれぞれ別で受けられる

sum = 0
outcome = {"参加費"=>1000, "ストラップ代"=>1000, "懇親会参加費"=>4000}
outcome.each do |item, price|
  sum += price
end
puts "合計 : #{sum}"

定形の処理を隠す

  • 後処理を確実に実行させる使い方 Ex) File.openメソッド -> ブロックを受け取るとファイルオブジェクトをブロック変数として一度だけブロックを起動する
File.open("sample.txt") do |file|
  file.each_line do |line|
    print line
  end
end
  • File.openメソッドにブロックを与えた場合は、ブロック内処理が終了してメソッドから抜ける時に自動的にファイルが閉じられるためfile.closeを書く必要が無い
    -> 内部的に以下の処理が行われている
file = File.open("sample.txt")
begin
  file.each_line do |line|
    print line
  end
ensure
  file.close
end

計算の一部を差し替える

  • Array.sortのように要素の比較方法をブロックを指定する

Ex)

array = ["Ruby", "Perl", "PHP", "Python"]
sorted = array.sort{ |a, b| a <=> b}
  • 並べ替えの処理の共通部分はメソッドで、並べ替えの順序などの目的によって異なる処理だけを差し替えるためにも使われる

ブロック付きメソッドを作る

ブロック変数を渡す、ブロックの結果を得る

Ex)

def total(from, to)
  result = 0               # 合計の値
  from.upto(to) do |num|   # fromからtoまで処理する
    if block_given?        # ブロックがあれば
      result += yield(num) # ブロックで処理した値を足す
    else                   # ブロックがなければ
      result += num        # そのまま足す
    end
  end
  return result
end

p total(1, 10) # 1から10の和 => 55
p total(1, 10){|num| num ** 2} # 1から10の2乗の和 => 385
  • yieldはメソッドに与えられたブロックを実行する命令
    • yieldの引数の数とブロック変数の数は違っても構わない
    • ブロック変数のが多い場合はnil、足りない場合は値を受け取れないだけ

ブロックの実行を制御する

Ex)

n = total(1, 10) do |num|
  if num == 5
    break
  end
  num
end
  • 上記を実行すると、totalメソッドの結果はnilになる
    • ブロックの中でbreakを呼ぶとブロック付き呼び出しのところまで一気に戻ってくるため、結果を返す処理など飛ばされてしまう
    • 何か値を返したい場合はbreak 0のように引数を付けると戻り値とできる
  • nextを使った際は、その回は中断されるが続きは行われる

ブロックをオブジェクトとして受け取る

  • ブロックをオブジェクトとして受け取る事で、ブロックを受け取ったメソッドとは別の場所でブロックを実行したり、ブロックを別のメソッドに与えて実行したりできる
  • ブロックをメソッドとして持ち運ぶにはProcオブジェクトを使う
    • Procオブジェクトを作るには、Proc.newメソッドをオブジェクト付きメソッドとして呼び出す
    • ブロックの手続きは、Procオブジェクトに対してcallメソッドで呼び出す

Ex)

hello = Proc.new do |name|
  puts "Hello, #{name}."
end

hello.call("World")
hello.call("Ruby")

-> 結果
$ ruby proc.rb

Hello, World.
Hello, Ruby.
  • メソッドからメソッドにブロックを渡す時は、ブロックをProcオブジェクトとして変数で受け取って、次のメソッドに渡す
    • メソッド定義で最後の変数を&引数名(Proc引数)にすると、そのメソッドを呼び出す時に与えられたブロックは、自動的にProcオブジェクトに包まれる

Ex)

def total(from, to, &block)
  result = 0
  from.upto(to) do |num|
    if block
      result += block.call(num)
    else
      result += num
    end
  end
  return result
end

p total(1, 10) # => 55
p total(1, 10) {|num| num ** 2} # => 385
  • メソッド呼び出しの際にブロックが渡されてなければ、Proc変数はnilになる

ローカル変数とブロック変数

  • ブロックは名前空間をブロックの外側とも共有している
    • ブロックの外側で作られたローカル変数はブロックの中でも使える
    • 逆にブロックで初出の変数はブロックの外側に持ち出せない
    • ブロック内でのみ有効な変数(ブロックローカル変数)は、ブロック変数の後ろに「;」で区切って定義する

感想

  • なんかイマイチしっくり来ない。ブロックの良さというか…なんでそういう事するのかがまだ理解できてない。何見ればわかるんだろ
  • ブロック、プロック、block、proc、…
  • 書き方的な部分は終わって、次章からはクラスの詳細な説明とかに入るみたいなので、これからはrailsチュートリアルと並行してすすめる

以上