脳汁portal

アメリカ在住(だった)新米エンジニアがその日学んだIT知識を書き綴るブログ

メタプログラミングRuby 第3章 まとめ (ブロック)

第三章でのキーワード

ブロック

  • ブロックを定義できるのはメソッドを呼び出すときだけ
  • ブロックを受け取ったメソッドはyieldでブロックをコールバック(展開して利用)する
  • ブロックとはメソッドに渡す無名引数みたいなもの
  • ブロックもメソッドと同様に、最終行を評価した結果を返す
  • block_given?メソッドで、ブロックが送られたかどうか確認できる
def test_method
  puts block_given?
end
 
test_method # => false
test_method{"hogehoge"} # => true
  • ブロック = コード + 束縛

束縛

  • ブロックを定義したとき、その場所の束縛を取得する
  • ブロックをメソッドに渡すときは、その束縛も一緒にもっていく
  • 変数とかはブロックが定義されたときの値をみる(ブロックの引数は除く)
  • この特性を「クロージャ」という
def test_method2
  x = "x in Method"
  y = "y in Method"
  yield("yield argument")
end
 
x = "x out of Method"
y = "y out of Method"

test_method2{|y|
  puts x # => x out of Method
  puts y # => yield argument
}
  • ブロックの中で新しい束縛を作成することも出来、この中で宣言された変数はブロックが終了すると消えてしまう。

この変数をブロックローカル変数という

  • JavaC#には外部スコープの変数を参照できる仕組みがあるが、Rubyははっきりと区別されており、参照できないようになっている

プログラムがスコープを切り替える場所

  • クラス定義
  • モジュール定義
  • メソッド呼び出し

これらは、それぞれ、class, module, defといったキーワードではじまるので、それらはスコープゲート(新しいスコープのはじまる門・入り口)と呼ばれる
(余談だがこの辺はRubyのゴールドの試験によく出てきた気がする。このコードを実行すると変数にはどの値が入ってるかみたいな)

クラスやモジュールの定義のコードはすぐに実行されるが、メソッド定義のコードはメソッドを呼び出したときにはじめて実行される

class TestClass
  puts "Enter class!!" # => 実行される
 
  def test_method
    puts "Enter method!!" # => 実行されない
  end
end
 
module TestModule
  puts "Enter module!!" # 実行される
end

スコープゲート(class, def)をメソッド(class.new(), define_method())に置き換えると、他のスコープの変数を参照できるようになる
この技術をレキシカルスコープ、フラットスコープ、また単にスコープのフラット化という

Proc, lambda

ブロックの使用は

  1. コードを保管する
  2. 保管されたブロックを呼び出して実行する(yield)

この保管して呼び出すという方法は以下のものがある

  • ブロック
  • Proc = ブロックがオブジェクトになったmの
  • lambda = Procの変形
  • メソッド

Rubyではほぼ全てがオブジェクトだが、ブロックは違う。だから保存しておいて後で使うということが出来ない
=> Procを使えばいい

Proc

プロックにブロックを渡すことでオブジェクト化できる
オブジェクト = Proc.new{#ブロック}
Procを呼び出すのはProc.call

inc = Proc.new{|x| x -1}
 
###色々な処理
 
inc.call(2)

この技術を「遅延評価」という

ブロックをProcに変換する方法

  • proc()
  • lambda()

ブロックではなく、Procを使う場面

  • ブロックを受け取って、さらに他のメソッドに渡したいとき
  • ブロックをProcにして保管しておきたいとき
Procを ブロックとして受け取る

最後の仮引数の頭に&をつければブロックとして受け取るという意味になる

def teach_math(a, b, &operation)
  puts math(a, b, &operation)
end
 
def math(a, b)
  yield(a, b)
end
 
teach_math(2, 3){|x, y| x * y }  # => 6
Procをブロックにして送る

送るときに&をつける

def my_method(greeting)
  puts "#{greeting} #{yield}"
end
 
my_proc = proc{"Bill"}
my_method("Hello", &my_proc) # => Hello Bill

procとlambdaの違い

portaltan.hatenablog.com