obj.instance_eval(&block)とblock.call(obj)の違いを確認
お金 | 仕事 | 勉強 | プライベート | 健康 | 心
プログラミング関連のコンテンツ
C言語/C++入門 | Ruby入門 | Python入門 | プログラミング全般
=追記(2010/07/23)
Rubyベストプラクティスのひとり読書。理解が難しかった部分を中心にまとめています。
書籍から引用しすぎと思われたページ、理解したページなどを削除して、当初より公開ページを減らしました。すいません。
読書のまとめ記事は難しいと感じたので、今後はブログで読書のまとめは書かないでおきます。差し障りがあれば現存するページも削除致しますので予めご了承下さい。
=追記ここまで
p70~p71にかけて、instance_evalとProc#callの使いかたの違いがよく分からなかったので、コードを書いて実験してみました。特に、
block.arity < 1 ? pdf.instance_eval(&block) : block.call(pdf)
という部分が分かりにくかった。
Proc#arity はブロック(block)に与えられたブロック引数の数を返す。
Procオブジェクトについて
実際のコードの前にProcオブジェクトについて少々。
クラス/メソッドの定義 – Rubyリファレンスマニュアルを見ると、
最後の仮引数の直前に & があるとこのメソッドに与えられているブロッ クが手続きオブジェクト(Proc)としてこの引数に格納されます。これは、 イテレータを定義する方法の一つです。(イテレータを 定義する代表的な方法は yield を呼び出すことです。 他に Proc.new/proc を使う方法などもありま す。) ブロックが与えられなかった場合のブロック引数の値はnilです。
とありますので、def hoge(&block); fuga; end みたいなメソッドを定義したたらblockにはProcオブジェクトが入ることになる。
obj.instance_eval(&block)とblock.call(obj)の違いを確認するコード
では、実験用のコード。
class Introduction
def self.output_1(&block)
intro = Introduction.new
block.call(intro)
end
def self.output_2(&block)
intro = Introduction.new
intro.instance_eval(&block)
end
def speech
print "My name is ... "
end
end
class Member
def initialize
@name = "Jotaro"
end
def name
@name
end
# 実行可能
def his_name_1
Introduction.output_1 do |intro|
intro.speech
print "#{name}\n"
end
end
# undefined local variable or method `speech'
def his_name_2
Introduction.output_1 do
speech # レシーバとなるブロック引数なしだとエラー
print "#{name}\n"
end
end
# undefined local variable or method `name'
def his_name_3
Introduction.output_2 do |intro|
intro.speech
print "#{name}\n"
end
end
# undefined local variable or method `name'
def his_name_4
Introduction.output_2 do
speech # レシーバとなるブロック引数なしでも動く
print "#{name}\n"
end
end
end
case ARGV[0].to_i
when 1
Member.new.his_name_1
when 2
Member.new.his_name_2
when 3
Member.new.his_name_3
when 4
Member.new.his_name_4
else
puts '"warn: set arg 1 or 2 or 3 or 4"'
end
実行結果
引数で、1~4を与えることで実行するメソッドを変更する。
>ruby 71.rb 1
My name is ... Jotaro
>ruby 71.rb 2
71.rb:39:in `his_name_2': undefined local variable or method `speech' for #<Member:0x483efb0 @name="Jotaro"> (NameError)
from 71.rb:6:in `call'
from 71.rb:6:in `output_1'
from 71.rb:38:in `his_name_2'
from 71.rb:66
>ruby 71.rb 3
My name is ... 71.rb:48:in `his_name_3': undefined local variable or method `name' for #<Introduction:0x283ee54> (NameError)
from 71.rb:11:in `instance_eval'
from 71.rb:11:in `output_2'
from 71.rb:46:in `his_name_3'
from 71.rb:68
>ruby 71.rb 4
My name is ... 71.rb:56:in `his_name_4': undefined local variable or method `name' for #<Introduction:0x283ee54> (NameError)
from 71.rb:11:in `instance_eval'
from 71.rb:11:in `output_2'
from 71.rb:54:in `his_name_4'
from 71.rb:70
コードにコメントしたエラーが出ている。
obj.instance_eval(&block) の場合は、objのコンテキストで評価されるため、囲まれたスコープにあるローカル変数以外(nameメソッド)にはアクセス出来ません。
ブロック引数で渡したレシーバを明示的に書かなきゃいけない手間はありますが、Proc#callだけでいいような気もするのだけど気のせいかな・・・
- - 関連記事 -
- プロキシオブジェクトの例で関数型プログラミングを理解
- 無名クラスを継承するRubyの動的機能
- method_missing()とsend()でフック(処理を捕捉)
- コードブロックを用いたサーバーとクライアントの例
- メソッド名とシグニチャを統一して美しいAPI設計
- mustメソッドで読みやすいテスト