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メソッドで読みやすいテスト